Un exemple de calcul faux

pgl10 Messages postés 387 Date d'inscription samedi 18 décembre 2004 Statut Non membre Dernière intervention 3 septembre 2024 - 15 août 2024 à 09:48
pgl10 Messages postés 387 Date d'inscription samedi 18 décembre 2004 Statut Non membre Dernière intervention 3 septembre 2024 - 17 août 2024 à 13:35

Bonjour,

Voici un petit calcul simple effectué en langage C++

#include <iostream>
#include <iomanip>
    
// Avec : u(0) = 3/2 et u(1) = 5/3
// calcul de la suite : u(n+2) = 2003 - 6002/u(n+1) + 4000/(u(n+1)*u(n))
// => u(2) = 9/5 , u(3) = 17/9 , u(4) = 33/17 , u(5) = 65/33 , ...
    
int main() {
    double x = 3.0/2.0;
    std::cout << "u(0) =  " << std::setprecision(13) << x << std::endl;
    double y = 5.0/3.0;
    std::cout << "u(1) =  " << std::setprecision(13) << y << std::endl;
    for(int i=2; i < 17; i++) {
        double z = 2003.0 - 6002.0/y + 4000.0/(x*y);
        if(i<10) std::cout << "u(" << i << ") =  " << std::setprecision(13) << z << std::endl;
        else     std::cout << "u(" << i << ") = " << std::setprecision(13) << z << std::endl;
        x = y;
        y = z;
    }
    return 0;
} 

et voici l'affichage du résultat obtenu

u(0) =  1.5
u(1) =  1.666666666667
u(2) =  1.8
u(3) =  1.888888889091
u(4) =  1.941176684635
u(5) =  1.969917499463
u(6) =  2.208511176929
u(7) =  204.748692622
u(8) =  1982.531859027
u(9) =  1999.982412248
u(10) = 1999.999982429
u(11) = 1999.999999982
u(12) = 2000
u(13) = 2000
u(14) = 2000
u(15) = 2000
u(16) = 2000

Mais il y a un problème. On peut assez facilement démontrer
que u(n) = 2 - 1/(1+2^n) : ce qui démontre la convergence
théorique de u(n) vers 2.

Il y a-t-il une modification plus ou moins simple
du code source qui permet de savoir que le résultat est faux
ou bien qui permet de calculer correctement les nombres u(n) ?

Merci, pgl10


2 réponses

pgl10 Messages postés 387 Date d'inscription samedi 18 décembre 2004 Statut Non membre Dernière intervention 3 septembre 2024 11
16 août 2024 à 06:36

Bonjour,

Les nombres flottants sont représentés en précision limitée avec un arrondi.
Il en résulte que leurs opérations arithmétiques sont approximatives.
Nous le savons très bien, mais on l'oublie souvent.
Cet exemple bien connu est là pour nous le rappeler.
Une solution du problème présenté consiste à utiliser un langage de programmation
qui n'utilise pas les nombres flottants pour représenter les fractions,  mais
c'est au prix de la durée des calculs fortement augmentée, pgl10


0
pgl10 Messages postés 387 Date d'inscription samedi 18 décembre 2004 Statut Non membre Dernière intervention 3 septembre 2024 11
17 août 2024 à 13:35

Bonjour,
    
MPIR est une bibliothèque utilisable en C++
qui a le type mpf_class pour les flottants en précision sans limite
et le type mpq_class pour les fractions en précision sans limite.
    
Voici par exemple le programme écrit avec l'utilisation des mpq_class.

#include <iostream>
#include <iomanip>
#include "mpirxx.h"

// Avec : u(0) = 3/2 et u(1) = 5/3
// calcul de la suite : u(n+2) = 2003 - 6002/u(n+1) + 4000/(u(n+1)*u(n))
// => u(2) = 9/5 , u(3) = 17/9 , u(4) = 33/17 , u(5) = 65/33 , ...

int main (void) { 
    mpq_class x(3,2);
    std::cout << "u(0) =  " << std::setprecision(13) << x.get_d() << std::endl;
    mpq_class y(5,3);
    std::cout << "u(1) =  " << std::setprecision(13) << y.get_d() << std::endl;
    for(int i=2; i < 51; i++) {
        mpq_class z = 2003 - 6002/y + 4000/(x*y);
        if(i<10) std::cout << "u(" << i << ") =  " << std::setprecision(13) << z.get_d() << std::endl;
        else     std::cout << "u(" << i << ") = " << std::setprecision(13) << z.get_d() << std::endl;
        x = y;
        y = z;
    }
}

    
Pour compiler ce programme il suffit d'ajouter avec le source
les quatre fichiers : mpir.h, mpir.lib, mpirxx.h et mpirxx.lib
issus de mpfr_mpir_x86_x64_msvc2010.zip en provenance
de : http://www.holoborodko.com/pavel/mpfr/
puis d'utiliser le mode Multithread (/MT) et les deux bibliothèques.
A l'exécution, la convergence vers 2 est vérifiée.
    
Et voici l'affichage du résultat obtenu.

u(0) =  1.5
u(1) =  1.666666666667
u(2) =  1.8
u(3) =  1.888888888889
u(4) =  1.941176470588
u(5) =  1.969696969697
u(6) =  1.984615384615
u(7) =  1.992248062016
u(8) =  1.996108949416
u(9) =  1.998050682261
u(10) = 1.999024390244
u(11) = 1.999511957052
u(12) = 1.999755918965
u(13) = 1.999877944587
u(14) = 1.999938968569
u(15) = 1.999969483353
u(16) = 1.999984741444
u(17) = 1.999992370664
u(18) = 1.999996185317
u(19) = 1.999998092655
u(20) = 1.999999046327
u(21) = 1.999999523163
u(22) = 1.999999761581
u(23) = 1.999999880791
u(24) = 1.999999940395
u(25) = 1.999999970198
u(26) = 1.999999985099
u(27) = 1.999999992549
u(28) = 1.999999996275
u(29) = 1.999999998137
u(30) = 1.999999999069
u(31) = 1.999999999534
u(32) = 1.999999999767
u(33) = 1.999999999884
u(34) = 1.999999999942
u(35) = 1.999999999971
u(36) = 1.999999999985
u(37) = 1.999999999993
u(38) = 1.999999999996
u(39) = 1.999999999998
u(40) = 1.999999999999
u(41) = 2
u(42) = 2
u(43) = 2
u(44) = 2
u(45) = 2
u(46) = 2
u(47) = 2
u(48) = 2
u(49) = 2
u(50) = 2


D'autres solutions équivalentes qui utilisent les fractions
sans utiliser le nombres flottants habituels sont possibles.
Bon amusement, pgl10


0
Rejoignez-nous