Approximations indésirables avec Python

Discutez d'informatique ici !
Anonyme

Approximations indésirables avec Python

par Anonyme » 27 Mar 2009, 17:55

Bonjour à vous ! C'est mon premier message sur ce forum même si je le lis depuis quelques jours. =P Je ne sais pas s'il est de coutume de se présenter ou pas pour l'occasion, mais je vais essayer de ne pas encombrer ce message et d'aller au but.

J'ai ecrit plusieurs programmes en Python, dans lesquels il y a des boucles "for" qui vont, admettons, de 1 à (int(ifin) +1), ifin étant un float. J'ai remarqué que les effets que je voulais obtenir à la fin de ces boucles n'étaient parfois pas les bons... j'en ai déduit que la boucle s'arrêtait trop tôt. Après ma petite enquête, j'ai pu vérifier que c'était le cas. Et voilà pourquoi, j'illustre avec un cas concret :

Je fais un print à la fin de la boucle pour voir où ça ne va pas, j'affiche ifin et int(ifin). Ca me donne :
100.0 99
au lieu de ce que je voudrais bien avoir... :
100.0 100

Mais ce n'est pas tout, car cette boucle (avec i allant de 1 à int(ifin)+1) se trouve à l'intérieur d'une autre grande boucle qui à chaque itération fait changer ifin en le multipliant par 10.
Supposons que cette autre grande boucle n'itère qu'une fois, avec ifin = 100.0. Là j'obtiens bien int(ifin) = 100. Mais supposons que cette grande boucle itère deux fois ou plus, et supposons que lors de la première itération on ait ifin = 10.0 et lors de la deuxième ifin = 100.0 (multiplication par 10 comme je l'ai dit), j'obtiens int(ifin) = 99.

Je ne comprend pas du tout ce comportement. J'aimerais bien vous montrer mes programmes, mais malheureusement je n'ai pas internet chez moi et j'ai oublié ma clé USB, donc j'y penserai à mon prochain passage sur le forum, mais j'ai essayé de donner tous les éléments qui me semblaient importants sans la connaissance explicite de mes programmes. Désolé !

Je ne connais vraiment pas grand chose en informatique (je suis étudiant en physique en fait), alors si quelqu'un aurait une idée de la raison de ces valeurs de int(ifin) trop petites, je suis prenneur !

Merci d'avance à tout ceux qui prendront le temps de me lire, j'ai bien conscience que ça peut-être casse-tête (ce sera pire avec mes programmes sous les yeux j'imagine haha).

Bonne journée ! =)



PrépaQuébec
Membre Relatif
Messages: 253
Enregistré le: 26 Juin 2007, 13:57

par PrépaQuébec » 28 Mar 2009, 01:33

Salut,

j'attends le code!

Anonyme

par Anonyme » 28 Mar 2009, 14:45

Et voilà, je peux le poster maintenant. En fait, il s'agit de l'algorithme d'Euler amélioré.

Je l'applique ici à une équation différentielle particulière : dx/dt = -x (avec la condition x(0) = 1)
J'intègre donc l'équation sur [0,1] avec l'algorithme, et je compare la valeur trouvée ainsi au point t = 1 avec la valeur analytique qui est exp(-1). J'ai ainsi l'erreur commise par cette méthode. (fonction euler_ameliore_erreur)

Je fais cela plusieurs fois en faisant varier le pas de t (deltat dans mon programme) de 0.1 à 0.01 puis 0.001 dans l'exemple ci-dessous. (fonction erreur)

Code: Tout sélectionner
#!/usr/bin/env python


from math import *


def cb (deltat, t0, tfin) :
    return (tfin - t0)/deltat


def f (x) :
    return -x


def xn_e (x, deltat) :
    return x + f(x) * deltat


def xn (x, deltat) :
    return x + ((f(x) + f(xn_e(x, deltat)))/2.0) * deltat


def euler_ameliore_erreur (deltat, t0, tfin, x0) :
    ifin = cb(deltat, t0, tfin)
    t = t0 + deltat
    x = x0
    for i in range(1, int(ifin) + 1) :
        x = xn(x, deltat)
        t += deltat
    print deltat, '\t', abs(x - exp(-1))


def erreur (deltat_i, deltat_f) :
    if (deltat_i = deltat_f) :
        euler_ameliore_erreur(d, 0., 1., 1.)
        d *= 0.1


erreur(0.1, 0.001)



Ca ne me renvoie pas ce à quoi je m'attends, alors j'ai fait cela pour voir d'où vient le problème :

Je rajoute la ligne suivante à la fonction euler_ameliore_erreur :
Code: Tout sélectionner
print "ifin =", ifin, "int(ifin) =", int(ifin)


Et cela me renvoie :
0.1 0.00066154366211
ifin = 10.0 int(ifin) = 10
0.01 0.00370342708389
ifin = 100.0 int(ifin) = 99
0.001 0.000368124801438
ifin = 1000.0 int(ifin) = 999


Maintenant, la même chose, mais avec un pas de 0.01 seulement (je lance erreur(0.01, 0.01) au lieu de erreur(0.1, 0.001)) :
0.01 6.17754474969e-06
ifin = 100.0 int(ifin) = 100

Exactement ce qu'il faut ! Contrairement à la fois précédente.
:hein:

Et voilà, vous avez pu voir que en passant d'un pas de 0.1 à 0.01 l'erreur obtenue abs(x - exp(-1)) augmente au lieu de diminuer, ce qui est complètement absurde en principe. La boucle d'itération dans euler_ameliore_erreur s'arrête une étape trop tôt à cause du fait que int(ifin) est évalué à 99 au lieu de 100 et 999 au lieu de 1000. Et il y a p'tet encore d'autres choses bizarres que j'ai oublié de citer.

Je n'ai pas expliqué en détails à quoi servent toutes les fonctions de mon programme, mais si vous connaissez un peu la méthode d'Euler pour ces résolutions numériques d'équa diff vous pouvez peut-être comprendre, sinon n'hésitez pas à demander, j'essaierai d'être le plus clair possible. ^^'

Evidemment, je rencontre des problèmes similaires avec mes programmes utilisant la méthode d'Euler traditionnelle (sans aucune amélioreation) ou encore avec la méthode de Runge-Kutta. (les trois programmes ont à peu près la même structure)

Merci encore ! =)

bombastus
Membre Complexe
Messages: 2295
Enregistré le: 29 Nov 2007, 21:35

par bombastus » 29 Mar 2009, 22:38

Salut,

je ne sais pas exactement le principe du cast en entier avec python, mais apparemment l'erreur que l'on peut observer sur certains flottants est récurrente avec python (cf http://www.network-theory.co.uk/docs/pytut/FloatingPointArithmeticIssuesandLimitations.html )

Je n'ai pas regardé ton programme dans le détail, mais l'utilisation de round te permettra peut-être de régler ton problème. round permet de faire un arrondi à l'entier le plus proche (round(3.4)=3.0 round(3.5)=4.0). La valeur renvoyé est un flottant, donc il faut garder la syntaxe int(round(...))

Ce n'est qu'une piste, je te laisse vérifier et faire les tests...

Anonyme

par Anonyme » 30 Mar 2009, 17:36

Merci beaucoup pour ce lien assez instructif.

Round peut en effet aider pas mal dans cette situation huhu.

Sinon, Doraki m'a suggéré de définir le nombre d'itérations à faire avant le pas, de manière à ce que ce soit un entier tout simplement... C'est simple, et ça marche. Ca perd un peu en généralité si je veux itérer autrement qu'en divisant le pas par 10 à chaque fois, mais ici, ça marche très bien.

 

Retourner vers ϟ Informatique

Qui est en ligne

Utilisateurs parcourant ce forum : Aucun utilisateur enregistré et 4 invités

Tu pars déja ?



Fais toi aider gratuitement sur Maths-forum !

Créé un compte en 1 minute et pose ta question dans le forum ;-)
Inscription gratuite

Identification

Pas encore inscrit ?

Ou identifiez-vous :

Inscription gratuite