IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Mathématiques Discussion :

Algorithme récursif de calcul de moyenne


Sujet :

Mathématiques

  1. #1
    Membre éprouvé

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 116
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 116
    Points : 1 111
    Points
    1 111
    Par défaut Algorithme récursif de calcul de moyenne
    Bonjour, je veux faire un programme en C de calcul de moyenne, mais je suis confronté à un problème de limitation de type.

    En fait, j'ai un tableau de unsigned long de 5000 points, et je veux réaliser une moyenne sur ce tableau. La difficulté est que la somme directe a toutes les chances de ne pas fonctionner, car la somme de 5000 unsigned long risque ne ne pas tenir dans un unsigned long. Je voudrai faire un algo du type
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    double mean_extreme(unsigned long * tab_in , unsigned long i, unsigned long size);
    {
         double moyenne_calculee;
         while(i!=(size/2)-1)
         {
              moyenne_calculee=((tab_in[i] + tab_in[size-i-1])/2.0 + mean_extreme(tab_in,i+1,size))/2.0;
         }
         return moyenne_calculee;
    }
    Ça a une chance de marcher ?

  2. #2
    Rédacteur
    Avatar de Zavonen
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    1 772
    Détails du profil
    Informations personnelles :
    Âge : 77
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 772
    Points : 1 913
    Points
    1 913
    Par défaut
    Une moyenne est un isobarycentre.
    Tu peux appliquer le théorème d'associativité des barycentres.
    Qui dit que le barycentre de
    (P1,m1) .... (Pn,mn)
    est le barycentre du
    barycentre de
    (P1,m1) .... (Pn-1,mn-1) avec la masse m1+m2+ ..mn-1
    avec le point pondéré (Pn,mn)
    dans ce cas de figure la moyenne de
    x1, .....,xn
    est le barycentre de la moyenne de
    x1,...xn-1 avec la masse n-1
    avec le point (xn,1)
    Il suffit d'itérer:
    Moyenne des deux premiers
    Moyenne pondérée de cette moyenne avec la masse 2 et du troisième avec la masse 1
    Moyenne pondérée de cette moyenne avec la masse 3 et du qautrième avec la masse 1
    And so on till the end

  3. #3
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 084
    Points
    16 084
    Par défaut
    Citation Envoyé par kromartien Voir le message
    La difficulté est que la somme directe a toutes les chances de ne pas fonctionner, car la somme de 5000 unsigned long risque ne ne pas tenir dans un unsigned long.
    c'est pour ca qu'on a inventé les "double"...
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    double moyenne_calculee=0;
    for(int i=0;i<size;i++)
        moyenne_calculee+=tab_in[i];
    moyenne_calculee/=size;

  4. #4
    Membre éprouvé

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 116
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 116
    Points : 1 111
    Points
    1 111
    Par défaut
    Pour la pondération des moyennes successives, ça me paraît être la bonne méthode.
    Et autrement, est-ce que l'appel récursif est correct, l'appel va-t-il marcher ? Ce serait beau d'utiliser la récursion.

  5. #5
    Inactif  
    Inscrit en
    Mars 2006
    Messages
    352
    Détails du profil
    Informations forums :
    Inscription : Mars 2006
    Messages : 352
    Points : 292
    Points
    292
    Par défaut
    Bonjour,
    Citation Envoyé par kromartien Voir le message
    Bonjour, je veux faire un programme en C de calcul de moyenne, mais je suis confronté à un problème de limitation de type.

    En fait, j'ai un tableau de unsigned long de 5000 points, et je veux réaliser une moyenne sur ce tableau. La difficulté est que la somme directe a toutes les chances de ne pas fonctionner, car la somme de 5000 unsigned long risque ne ne pas tenir dans un unsigned long. Je voudrai faire un algo du type
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    double mean_extreme(unsigned long * tab_in , unsigned long i, unsigned long size);
    {
         double moyenne_calculee;
         while(i!=(size/2)-1)
         {
              moyenne_calculee=((tab_in[i] + tab_in[size-i-1])/2.0 + mean_extreme(tab_in,i+1,size))/2.0;
         }
         return moyenne_calculee;
    }
    Ça a une chance de marcher ?
    J'ai déroulé ton code à travers un tout petit exemple mais malheureusement ça marche pas !

    Cependant, je voulais faire quelques remarques et suggestions à propos de ce code :
    - Le compteur i n'est pas initialisé,
    - La variable i n'a pas besoin d'être du type long, un simple int suffirait,
    - As-tu vraiment besoin d'une version récursive ? Autrement dit une version itérative ne t'intéresse pas ?
    -Je pense que l'utilisation l'opérateur de comparaison < serait plus propre et plus logique !


    Citation Envoyé par kromartien Voir le message
    Pour la pondération des moyennes successives, ça me paraît être la bonne méthode.
    À mon avis, pourquoi ne pas changer l'échelle : une sorte de petite codification en divisant tous les éléments du tableau par 10, 100, 1000, 10000, ...etc tout dépend des éléments s'ils sont petits, grands, trop grands, ...etc
    Et calculer la moyenne tranquillement sans le moindre souci !

    À bientôt.

  6. #6
    Membre éprouvé

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 116
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 116
    Points : 1 111
    Points
    1 111
    Par défaut
    J'ai réécrit la version récursive et fait une version itérative :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     
     
    double mean_extreme(unsigned long * tab_in , unsigned long i, unsigned long size)
    {
    	if(i<(size/2))
    	{
    		double moyenne_calculee;
    		moyenne_calculee=((tab_in[i] + tab_in[size-i-1])/2.0 + mean_extreme(tab_in,i+1,size))/2.0;
    	}
    	else
    	{
    		return moyenne_calculee;
    	}
    }
     
    double mean_ponderee(unsigned long * tab_in, unsigned long SIZE)
    {
    	unsigned long i;
    	double current_mean=tab_in[0];
    	for(i=0;i<SIZE-1;i++)
    	{
    		current_mean = (current_mean * ((i+1.0)/(i+2.0))) + tab_in[i+1])*(1.0/(i+2.0));
    	}
    	return current_mean;
    }

  7. #7
    Membre éprouvé Avatar de Nemerle
    Inscrit en
    Octobre 2003
    Messages
    1 106
    Détails du profil
    Informations personnelles :
    Âge : 54

    Informations forums :
    Inscription : Octobre 2003
    Messages : 1 106
    Points : 1 213
    Points
    1 213
    Par défaut
    z'etes un peu fou les gars... pseudocode a tout bien résumé...

  8. #8
    Membre éprouvé

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 116
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 116
    Points : 1 111
    Points
    1 111
    Par défaut
    D'accord, j'ai utilisé ces deux algorithmes en concurrence :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     
    /*(1)*/
    double mean_ponderee(unsigned long * tab_in, unsigned long SIZE)
    {
    	unsigned long i;
    	double current_mean=tab_in[0];
    	for(i=0;i<SIZE-1;i++)
    	{
    		current_mean = (current_mean * ((i+1.0)/(i+2.0))) + tab_in[i+1]*(1.0/(i+2.0));
    	}
    	return current_mean;
    }
     
    /*(2)*/
    double mean_classic(unsigned long * tab_in, unsigned long SIZE)
    {
    	unsigned long i;
    	double mean=0;
    	for(i=0;i<SIZE;i++)
    	{
    		mean=mean+tab_in[i];
    	}
    	mean = mean/SIZE;
    	return mean;
    }
    Et j'obtiens de part et d'autre le même résultat.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    22.498000(1), 22.498000(2)
    Ça marche, voilà, le premier a tout de même l'avantage de prévenir un dépassement de capacité de type.

    Merci beaucoup.

  9. #9
    Rédacteur
    Avatar de Zavonen
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    1 772
    Détails du profil
    Informations personnelles :
    Âge : 77
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 772
    Points : 1 913
    Points
    1 913
    Par défaut
    current_mean = (current_mean * ((i+1.0)/(i+2.0))) + tab_in[i+1]*(1.0/(i+2.0));
    Voilà une ligne qui coûte cher:
    trois additions type double
    deux divisions type double
    deux multiplications type double

    alors que :
    (current_mean*(i+1)+tab_in[i+1])/(i+2) ferait la même chose
    avec seulement
    une mutiplication double
    une addition double
    une addition int
    une division double

  10. #10
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 084
    Points
    16 084
    Par défaut
    Citation Envoyé par kromartien Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    double mean_ponderee(unsigned long * tab_in, unsigned long SIZE)
    {
    	unsigned long i;
    	double current_mean=tab_in[0];
    	for(i=0;i<SIZE-1;i++)
    	{
    		current_mean = (current_mean * ((i+1.0)/(i+2.0))) + tab_in[i+1]*(1.0/(i+2.0));
    	}
    	return current_mean;
    }
    Ça marche, voilà, le premier a tout de même l'avantage de prévenir un dépassement de capacité de type.
    Curieuse remarque vu que tous les calculs sont effectués en type "double".

  11. #11
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Points : 17 923
    Points
    17 923
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Zavonen Voir le message
    Voilà une ligne qui coûte cher:
    trois additions type double
    deux divisions type double
    deux multiplications type double

    alors que :
    (current_mean*(i+1)+tab_in[i+1])/(i+2) ferait la même chose
    avec seulement
    une mutiplication double
    une addition double
    une addition int
    une division double
    et même :

    j = i + 1
    current_mean*(j+tab_in[j])/(i+2)

    avec ton nombre d'opération (tu as oublié dans ton cas une addition int)

  12. #12
    Membre éprouvé

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 116
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 116
    Points : 1 111
    Points
    1 111
    Par défaut
    Merci d'avoir corrigé ma formule, c'est vrai que j'ai fait ça de manière irréfléchie.

    Le format de représentation des double peut-il vraiment contenir la gamme entière des unsigned long ? Ne risque-t-on pas d'arriver à des erreurs d'arrondis avec la méthode "brute" contrairement à la méthode de calcul de moyennes successives ??

  13. #13
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 084
    Points
    16 084
    Par défaut
    Citation Envoyé par kromartien Voir le message
    Le format de représentation des double peut-il vraiment contenir la gamme entière des unsigned long ? Ne risque-t-on pas d'arriver à des erreurs d'arrondis avec la méthode "brute" contrairement à la méthode de calcul de moyennes successives ??
    Déja, tu as pratiquement tous tes calculs qui sont effectués en précision "double":

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    double current_mean
    int i
    long tab_in[]
     
    (current_mean*(i+1)+tab_in[i+1])/(i+2)
    calculs 1:
    (i+1) --> int+int -> int
    (i+2) --> int+int -> int

    calcul 2:
    current_mean*(i+1) --> double*int --> double

    calcul 3:
    current_mean*(i+1)+tab_in[i+1] --> double + long --> double

    calcul 4:
    (current_mean*(i+1)+tab_in[i+1])/(i+2) --> double/int --> double
    De plus, j'ai l'impression que la methode des moyennes successives va generer une erreur de plus en plus grande du fait des multiplications successives par (i+1)/(i+2)

  14. #14
    Membre éprouvé

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 116
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 116
    Points : 1 111
    Points
    1 111
    Par défaut
    Les erreurs d'arrondi n'interviennent que lors d'un dépassement de la capcité de représentation du type. Je divise au final par i+2, mais le nombre obtenu n'est jamais irrationnel, le nombre de décimale est limité, et apparemment il n'excède pas ,dans le cas des moyennes pondérées successives, le nombre de décimales permis par le type double.

    Par contre, lorsque je fais la somme directe, je peux être amené à supposer que la somme de tous ces unsigned long va donner un nombre dont la représentation de la mantisse peut excéder les capacités d'un double.

    En tous cas, je divise des nombres entiers, pour une moyenne sur ~500 nombres. Ça ne fait pas plus de 1/500 comme valeur minimale représentée par un double, càd 0.002, c'est largement faisable pour un type double si la moyenne n'excède pas [un grand nombre]

    j'avais juste un doute sur une somme de beaucoup de grands nombres, on peut aboutir à un chiffre qui dépasse la plage de représentation de la mantisse d'un double. C'est pour ça que je cherchais un algorithme qui ne faisait pas une somme irréfléchie, qui fait juste une sommation sur un nombre de digits limité, pour prévenir le dépassement de la capacité de type.

  15. #15
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 084
    Points
    16 084
    Par défaut
    Citation Envoyé par kromartien Voir le message
    j'avais juste un doute sur une somme de beaucoup de grands nombres, on peut aboutir à un chiffre qui dépasse la plage de représentation de la mantisse d'un double. C'est pour ça que je cherchais un algorithme qui ne faisait pas une somme irréfléchie, qui fait juste une sommation sur un nombre de digits limité, pour prévenir le dépassement de la capacité de type.
    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    long value = 0x7fffffffffffffffL;  // max long value = 2^63-1
     
    // mean of 500 times the maximum "long" value
    // mean_1: weighted, mean_2:non-weighted
    double mean_1=value, mean_2=0;
    for(int i=0;i<500;i++) {
    	mean_1 = (mean_1*(i+1) + value)/(i+2);
    	mean_2 += value;
    }
    mean_2/=500;
     
    System.out.println("mean_1:"+(long)mean_1);
    System.out.println("mean_2:"+(long)mean_2);
    System.out.println("expected:"+(long)value);

    Résultats:
    mean_1:9223372036854775807
    mean_2:9223372036854775807
    expected:9223372036854775807
    Plus interessant, si au lieu de faire la moyenne des "2^63-1", on fait la moyenne des "2^63-2" on obtient:
    // avec: long value = 0x7ffffffffffffffEL;
    mean_1:9223372036854775807
    mean_2:9223372036854775807
    expected:9223372036854775806
    Comme la methode "pondéré" fait egalement des conversions en "double", on a la meme erreur d'arrondi.

  16. #16
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Points : 17 923
    Points
    17 923
    Billets dans le blog
    2
    Par défaut
    euh.. juste pour info....

    en 32 bits, double max = e+308 ... ça laisse de la marge non ?

  17. #17
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 084
    Points
    16 084
    Par défaut
    Citation Envoyé par souviron34 Voir le message
    euh.. juste pour info....

    en 32 bits, double max = e+308 ... ça laisse de la marge non ?
    Oui mais les "double 32bits (IEEE 704)" n'offrent que 52 bits de mantisse (=chiffres significatifs), contre 63 bits pour les "long" et 64 bits pour les "unsigned long".

  18. #18
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Citation Envoyé par souviron34 Voir le message
    euh.. juste pour info....

    en 32 bits, double max = e+308 ... ça laisse de la marge non ?
    Sauf que ça ne veut absolument pas dire que tu as une telle précision, tu n'as pas 308 digits de précision (ou 1000 bits de précision si tu préfères)... Il est bien plus intéressant de dire qu'on a 52 bits de mantisse (pour les double IEEE en tout cas), et 11 bits d'exposants (ce qui correspond à ton 10^308).

    Néanmoins effectivement, avoir peur de dépasser la précision des double pour son calcul de moyenne me semble indiquer qu'on a un problème de choix de type : si on veut vraiment un résultat exact et qu'on a de très grand nombres, on utilise une bibliothèque mathématique avec des types numériques de précision arbitraire, sinon, on utilise l'algorithme "naïf", et on accepte de perdre un tout petit peu en précision de temps en temps.

    --
    Jedaï

  19. #19
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Points : 17 923
    Points
    17 923
    Billets dans le blog
    2
    Par défaut
    Jedai tu n'as pas assez fait la sieste aujourd'hui...

    Je ne parle pas de précision.... Mais de l'objection soulevée par Kromartien sur les dépassements de capacité...

  20. #20
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 084
    Points
    16 084
    Par défaut
    Citation Envoyé par souviron34 Voir le message
    Je ne parle pas de précision.... Mais de l'objection soulevée par Kromartien sur les dépassements de capacité...
    La perte de précision fait partie du dépassement de capacité.

    Que le dépassement soit sur les 52 bits de mantisse ou les 11 bits d'exposant, quand ca dépasse: ca dépasse.

Discussions similaires

  1. Algorithme récursif et calcul de complexité
    Par Lithrein dans le forum Algorithmes et structures de données
    Réponses: 3
    Dernier message: 14/12/2011, 02h10
  2. Réponses: 4
    Dernier message: 14/12/2009, 21h31
  3. Réponses: 1
    Dernier message: 24/02/2009, 21h28
  4. [Erreur] algorithme qui calcul une moyenne
    Par quaresma dans le forum Algorithmes et structures de données
    Réponses: 29
    Dernier message: 24/04/2008, 21h58
  5. calculer une moyenne avec une requete externe
    Par allowen dans le forum Langage SQL
    Réponses: 3
    Dernier message: 27/01/2005, 17h02

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo