La moyenne du terrassier

publication: 20 mai 2024 / mis à jour 20 mai 2024

Read this page in english

 

article: 11 avril 2020 / mis à jour 18 avril 2020

Calcul de la moyenne

Nous avons tous appris à calculer une moyenne. Il suffit de faire la somme de N nombres et de diviser cette somme par N. Exemple:

Beaucoup d'autres combinaisons permettent d'aboutir à cette moyenne:

Commencez-vous à saisir le problème?

En calculant une moyenne brute, nous n'arrivons pas à savoir si les élèves s'améliorent, stagnent ou sont en baisse. Pour Mickael, il est évident que ses notes accusent une amélioration impressionnante, alors que Charles a eu une faiblesse en cours de trimestre. Pour Henri, c'est la chute libre.

La moyenne du terrassier

Nous allons voir un moyen astucieux pour calculer la moyenne d'une autre manière, un moyen qui va permettre en particulier de déterminer si une situation évolue ou stagne.

Prenons le cas d'un terrassier qui doit combler un terrain pour le rendre plat en vue de construire une terrasse:

Ici, le terrain, à partir du point A est plat jusqu'au point B, puis est en pente du point B au point C.

Le terrassier doit remplir avec du béton et des granulats la partie de terrain colorée en vert. La hauteur de matériaux à amener est de 10 cm aux points A et B, puis de 20 cm au point C.

Si on fait la moyenne des hauteurs en A, B et C, on aura comme résultat (10+10+20)/3=13.33..

Quelques mois plus tard, notre même terrassier a un chantier similaire, mais doit combler ce terrain:

Faisons la moyenne des hauteurs A, B et C: (10+20+10)/3=13.33..

Pourtant, il n'y a pas du tout - mais alors vraiment pas du tout - le même volume de matière à couler.

Pour calculer le volume de matière à amener, on peut faire un calcul de géométrie. Pour notre part, nous allons aborder le problème d'une autre manière.

Les moyennes de moyennes

Le point médian situé entre A et B a une hauteur qui sera la valeur moyenne des hauteurs mesurées en A et B. C'est pareil pour le point médian situé entre B et C. Retrouvons le calcul de ces hauteurs moyennes ici:

A B C
10  10  20
 10  15 

 

Nous obtenons deux moyennes qui semblent correspondre aux hauteurs de terrain à combler:

La moyenne de ces moyennes M1 et M2 sera (M1+M2)/2=12.5

Refaisons le calcul de ces moyennes, appliqué au second cas de terrassement:

A B C
10  20  10
 15  15 

 

Calcul des deux moyennes:

La moyenne de ces moyennes M1 et M2 sera (M1+M2)/2=15

Rien qu'en modifiant l'ordre des données initiales, nous n'obtenons pas la même moyenne de moyenne! Les résultats semblent correspondre à la hauteur moyenne de matière à amener pour aménager les terrains de notre terrassier...

Application générale

Appliquons ce calcul de moyennes de moyennes aux notes de nos élèves:

Mickael:

A B C
10  10  20
 10  15 
  12.5   

 

Charles:

A B C
16  10  14
 13  12 
  12.5   

 

Henri:

A B C
18  17  5
 17.5  11 
  14.25   

 

Pour des raisons de facilités, abrégons par Mt cette moyenne de moyennes.

Analysons ces résultats:

Formalisation

Pour calculer une moyenne Mt de 3 valeurs A, B et C, voici la formule générale:

Mt = ( ( ( A + B ) / 2 ) + ( ( B + C ) / 2 ) ) / 2
   = ( ( A + B + B + C ) / 2 ) / 2
   = ( A + 2B + C ) / 4

Nous avons donc un divisieur, ici 4.

Il n'est donc pas nécessaire de faire les moyennes intermédiaires. Il suffit de faire les sommes intermédiaires:

Henri:

A B C
18  17  5
 35  22 
  57   

 

Ici, le résultat final 57 / 4 = 14.25.

Le diviseur 4 est égal à 2 exp 2. 2 est égal au nombre d'éléments, ici 3, valeur à laquelle on soustrait 1.

Est-ce que ça fonctionne avec 4 valeurs? Faisons le test:

Charles:

A B C D
16  10  14  11
 13  12  12.5 
  12.5  12.25   
   12.375    

 

La valeur Mt est ici 12.375.

Le même tableau, mais avec les sommes:

A B C D
16  10  14  11
 26  24  25 
  50  49   
   99    

 

Avec 4 éléments, notre diviseur sera 2 EXP 4-1 = 8

99 / 8 = 12.375

Au passage, on remarquera que 99 est la somme des valeurs 50 et 49, ces valeurs indiquant une légère baisse.

La valeur 50 résulte de l'application de cette formule:

MtX = ( A + 2B + C ) / 4

La valeur 49 résulte de l'application de cette formule:

MtY = ( B + 2C + D ) / 4

La somme Mt, de valeur 99, résulte donc de cette formule:

Mt = ( ( ( A + 2B + C ) / 4 ) + ( ( B + 2C + D ) / 4 ) ) / 2
   = ( ( A + 3B + 3C + D ) / 4 ) / 2
   = ( A + 3B + 3C + D ) / 8

On retrouve ces facteurs multiplicatifs, pour ( A + 3B + 3C + D ) / 8, ici 1 3 3 1, dans le triangle de Pascal:

Dans le triangle de Pascal, la somme des termes sur la ligne de rang n (première ligne = rang 0) est égale à 2 EXP n. C'est cette somme des termes qui nous sert de diviseur pour calculer la valeur Mt. Exemple, pour le résultat 99, Mt = 99 / ( 1 + 3 + 3 +1 ), soit 99/8.

Application en langage FORTH

Ici, vous trouverez le développement, en langage FORTH, du calcul de la moyenne du terrassier. Il y a deux versions, une pour gForth qui traite les données nativement en 32 bits, l'autre pour FlashForth qui utilise une pile 16 bits. Les différences entre ces deux développements aboutissent aux mêmes résultats.

Pour gForth

Spécifique gForth

Le code source complet est disponible ici.

Nous définissons un tableau de n valeurs, ici 5, stockées dans le tableau initValues. gForth stocke ces valeurs au format 32 bits:

5 constant nbValues         \ number of initials values 
\ compile initials values 
create initValues  
    16 ,    10 ,    14 ,    11 ,   18 , 
\ display values in array 
: .values ( adr n ---) 
    0 do 
        dup i cell * +      \ calculate address of a value 
        @ cr .              \ fetch and display value 
    loop ; 

Exemple d'utilisation de .values. Ce mot affiche n valeurs d'un tableau:

initValues nbValues .values     \ display:
16
10
14
11
18  ok

On définit ensuite deux autres tableaux, calcBuffer qui servira à stocker les valeurs intermédiaires, finalValues qui stocke le résultat final:

create calcBuffer 
    nbValues cell * allot 
create finalValues  
    nbValues cell * allot 

Le mot finalToBuffer copie n valeurs depuis finalValues vers calcBuffer:

: finalToBuffer ( n ---) 
    cell * >r 
    finalValues calcBuffer r> cmove 
  ; 

La variable calcDepth sert à mémoriser le niveau des calculs a traiter. A chaque recalcul des moyennes, le contenu de cette variable est décrémenté.

Le mot calcAverage est chargé de calculer les moyennes intermédiaires. Si le contenu de la varaible calcDepth n'est pas égal à un, la fonction se réexcéute par récursivité:

\ calculate eartworker average 
variable calcDepth 
: calcAverage ( ---) 
    -1 calcDepth +! 
    calcDepth @  0 do 
        calcBuffer  i    cell * + @     \ get first value in buffer 
        calcBuffer  i 1+ cell * + @ +   \ get second value in buffer and add 
        finalValues i    cell * + !     \ store result 
    loop 
    calcDepth @ 1 >  
    if 
        calcDepth @ finalToBuffer 
        recurse 
    then 
  ; 

Le mot calculate lance une session de calcul de la somme finale à partir de laquelle on obtiendra la moyenne Mt.

: calculate ( ---) 
    \ move initial values in buffer 
    initValues calcBuffer nbValues cell * cmove 
    \ set initial value of calcDepth 
    nbValues calcDepth ! 
    \ start average calculation 
    calcAverage 
  ; 

Pour FlashFORTH

La version FORTH pour Flashforth utilise des données 16 bits. Il a donc été nécessaire de procéder à certains aménagements pour pouvoir traiter les données de même taille que sur gForth, c'est à dire des données 32 bits:

-average 
marker -average 
\ convert integer in double and compile 
: 2, ( n --- ) 
    s>d swap , , ; 
 
\ calculate a 32 bits offset, example: 
: 2offset ( n --- n' ) 
    cell 2* * ; 
 
\ calculate real address for 32 bits content of array, example: 
: 2addr.offset ( addr offset --- addr' ) 
    2offset + ; 

On retrouve notre tableau des données initiales dans initValues:

5 constant nbValues         \ number of initials values 
flash 
\ compile initials values 
create initValues  
    16 2,    10 2,    14 2,    11 2,   18 2, 

Les mots i et i+ sont définis pour palier à l'absence de la boucle do..loop sous FlashForth:

\ calculate index starting from 0 
ram 
variable startIndex 
: i ( --- i )   \ create word i that not defined in FlashForth 
    startIndex @ 
  ; 
: i+  ( ---)    \ increment index 
    1 startIndex +! 
  ; 

On retrouve ici le mot .values où la boucle DO..LOOP a été remplacé par une boucle for..next:

\ display values in array 
: .values ( adr n ---) 
    0 startIndex ! 
    for 
        dup                 \ duplicate initial address 
        i                   \ get loop index 
        2addr.offset        \ calculate offset in 2array     
        2@ d.               \ fetch and display value 
        i+ 
    next  
    drop  
  ; 
 
ram 
\ calculate eartworker average 
create calcBuffer 
    nbValues 2offset allot 
create finalValues  
    nbValues 2offset allot 
 
eeprom 
: finalToBuffer ( n ---) 
    2offset >r 
    finalValues calcBuffer r> cmove 
  ; 
 
ram 
variable calcDepth 
eeprom 

Puis dans le mot calcAverage, la récursivité fait place à une boucle begin..while..repeat, car sous FlashForth, la profondeur des piles de données et de retour sont très limitées:

: calcAverage ( ---) 
    begin 
        -1 calcDepth +! 
        0 startIndex ! 
        calcDepth @  for 
            calcBuffer  i    2addr.offset  2@ 
            calcBuffer  i 1+ 2addr.offset  2@  d+ 
            finalValues i    2addr.offset 2! 
            i+ 
        next 
        calcDepth @ 1 > 
    while 
        calcDepth @ finalToBuffer 
    repeat 
  ; 
 
: calculate ( ---) 
    initValues calcBuffer nbValues 2offset cmove 
    nbValues calcDepth ! 
    calcAverage 
  ; 

Résultat de l'execution de calculate:

initValues nbValues .values cr 16 10 14 11 18
 ok<#,ram>
calcBuffer 2 .values cr 99 103
 ok<#,ram>
finalValues 2@ d. cr 202
 ok<#,ram>

Analyse du résultat des calculs

Que ce soit pour gForth ou pour FlashForth, pour tester le calcul de la moyenne du terrassier pour les valeurs stockées dans le tableau initValues, il suffit de faire exécuter ces mots comme ceci:

calculate
initValues nbValues .values cr
calcBuffer 2 .values cr
finalValues @ . cr

Affichage de l'exécution sous gForth:

calculate  ok
  ok
initValues nbValues .values cr
16
10
14
11
18
 ok
calcBuffer 2 .values cr
99
103
 ok
finalValues @ . cr 202
 ok

Le mot calculate lance la séquence de calcul de la moyenne du terrassier.

La séquence initValues nbValues .values cr affiche les valeurs initiales contenues dans le tableau initValues: 16 10 14 11 18

La séquence calcBuffer 2 .values cr affiche la dernière paire de valeurs dont la somme sera la valeur à partir de laquelle on calculera la moyenne Mt, ici 99 103:

Et enfin, la séquence finalValues @ . cr affiche la somme finale à partir de laquelle on peut calculer la moyenne Mt, ici 202

La moyenne MT sera calculée selon la formule 202 / ( 2 EXP nbValues-1), soit 202/16.

A quoi ça peut servir?

C'est une excellente question et je vous remercie de l'avoir posée.

On avait commencé cet article en traitant des notes scolaires pour des élèves; La moyenne Mt n'a, en réalité, aucun intérêt pour traiter des moyennes d'élèves.

Par contre, le calcul de moyenne Mt peut s'avérer très utile dans beaucoup de domaines.

On va prendre un exemple très simple, le cas d'une chaudière de chauffage central qui doit se déclencher si la température d'une sonde descend en dessous de 19°C. Le souci est que cette sonde a tendance à se déclencher de manière intenpestive, ce qui déclenche de nombreux mise en route et arrêt de la chaudière du chauffage central.

L'idée est donc de traiter par moyenne Mt seulement les six dernières mesure de températures, ici des mesures réalisées toutes les 10 minutes:

Dans le premier cas, bien que la mesure soit transitoirement à 18°C (quatrième valeur), la moyenne Mt étant supérieur à 19, la chaudière ne s'allume pas.

Le second cas correspond aux trois dernières mesures de température de la première ligne, auxquelles viennent se rajouter le smesures 17 18 18. La moyenne Mt descend en dessous de 19 et la chaudière s'allumera.

La moyenne Mt s'appliquera donc à un échantillon limité de valeurs. Par exemple, pour suivre l'évolution de ventes de produits sur une semaine, on prendra les 7 dernières valeurs de vente. Si chaque jour, on récupère les chiffres sur 7 jours glissants, il sera possible d'obtenir une tendance à la baisse, à la hausse ou aucune tendance d'évolution de ces ventes.

Pour conclure, nous avons traité, pour gForth et FlashForth des données entières au format 32 bits. Le calcul Mt fonctionne aussi avec des valeurs négatives. Si votre version du langage Forth dispose des fonctions de calcul en virgule flottante, libre à vous d'adapter le code Forth.