Partage
  • Partager sur Facebook
  • Partager sur Twitter

Programmer fonction exponentielle

Python

Sujet résolu
2 octobre 2012 à 18:46:15

Bonjour, je viens de commencer le python et j'ai voulu essayer de programmer la fonction exponentielle sans utiliser de fonction déjà écrite. Il faut donc que j'écrive les fonctions factorielle et puissance sachant que <math>\(e^x = \sum_{n = 0}^{+\infty} \frac{x^n}{n!}\)</math> . Mon code ne marche pas et je ne comprends pas pourquoi.
def fact(x):
    if x == 0:
        return 1
    else:
        return x*fact(x-1)
    
def puissance(x,y):
    if y == 0:
        b = 1
    else:
        for i in y:
            b = x * b
    return b

def exp(x):
    e = 0
    for i in range(10):
        e = e + (puissance(x,i))/(fact(i))
    return e

Pouvez vous m'indiquer quel est le problème ? merci d'avance
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
2 octobre 2012 à 19:38:44

Bonjour,

La fonction puissance n'est pas bonne et donne une erreur ici: for i in y: (y n'est pas une séquence) et à la ligne suivante (b non initialisé). Ton code corrigé:

def puissance(x,y):
    b = 1
    if y != 0:
        for i in range(0,y):
            b *= x
    return b


La fonction exp est ok, mais il faut aller plus loin dans la boucle (j'ai pris 30). En fait, il faudrait un while permettant de s'arrêter lorsque 2 termes consécutifs deviennent très proches.

Avec tout ça, print exp(5) donne 148.413159103 ce qui est le bon résultat (le même que la fonction exp du module math).


Il reste que c'est une très mauvaise idée de programmer l'exponentielle comme ça: il faut calculer directement avec la relation de récurrence:

- 1er terme d'indice 0 = 1

- terme d'indice n = (terme d'indice (n-1)) * x/n

Faire comme ça est aussi un excellent exercice: essaie!
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
2 octobre 2012 à 19:41:06

Salut,

L'erreur est situé dans la fonction puissance. Tu remarqueras que non seulement elle plante, mais elle est inefficace.
Quand tu écris for i in y, tu voudrais que que i prenne la valeur de 0, puis de 1, ... jusqu'à y exclu ? Or for parcours une liste, et non un nombre. Il faut donc créer une liste contenant 0, 1, ... y - 1. Pour cela, utilise range(y).

Ensuite, tu effectues des calculs avec une variable b, qui n'existe pas. Python n'aime pas. Donc cela donne :

def puissance(x,y):
    b = 1
    #si y == 0, range(y) sera vide, et le for ne tournera pas
    for i in range(y):
        b *= x 
    return b


(si tu es sous python2, sous python3, je crois que ça marche tout seul.)
Et ensuite, dans la fonction exp, tu calcules (puissance(x,i))/(fact(i)), mais le numérateur et le dénominateur sont des nombres entiers, alors python croit que tu veux juste la division entière. Pour avoir les décimales, il faut convertir un des nombre en floattant, soit (puissance(x,i))/(float(fact(i))).

Voilà, tu as maintenant une fonction exp qui marche, lentement, mais qui marche. On peut accélérer ça si tu veux.


EDIT: tyrtamos m'a grillé.
  • Partager sur Facebook
  • Partager sur Twitter
2 octobre 2012 à 20:29:40

Merci j'ai compris. En fait j'ai programmé la fonction comme ceci car j'ai vu en cours de maths que la fonction exponentielle s'écrivait comme ça donc ça m'a donné l'idée de le faire.

Edit : Par contre exp(0) me renvoie 2.0
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 octobre 2012 à 11:27:45

Citation : mercurial17

Merci j'ai compris. En fait j'ai programmé la fonction comme ceci car j'ai vu en cours de maths que la fonction exponentielle s'écrivait comme ça donc ça m'a donné l'idée de le faire.

Edit : Par contre exp(0) me renvoie 2.0



Bizarre, chez moi j'ai bien 1.0

Autrement, le problème de ton code, c'est qu'à chaque tour de boucle dans exp, il recalcule fact(n) et puissance(x, n). Par exemple, il calcul fact(1), puis fact(2) qui recalcule fact(1), et fact(3) qui recalcule fact(2)...Pour de petits nombres, cela ne prend pas de temps, mais quand n tend vers l'infini, ça devient très lent.
Il y a le même problème pour les calculs de puissances.

Tyrtamos t'a donné une piste pour améliorer...
  • Partager sur Facebook
  • Partager sur Twitter
3 octobre 2012 à 12:48:00

OK merci je vais essayer l'autre méthode. Ce matin en cours d'info, le prof nous a demandé de programmer la fonction exponentielle (hasard !) mais il fallait envoyer deux paramètres, le deuxième étant le nombre de fois que l'on exécute la somme. Et il fallait faire autre chose que je n'ai pas compris : il nous a parlé de exponentielle(x,erreur). Alors je sais pas si il faut la programmer ou quoi.
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 octobre 2012 à 14:06:46

Bonjour,

L'erreur correspond à la remarque que j'avais faite plus haut: "En fait, il faudrait un while permettant de s'arrêter lorsque 2 termes consécutifs deviennent très proches".

Tu as une série dont les termes sont de plus en plus petits: tu dois en calculer "suffisamment" pour que le résultat (la valeur de l'exponentielle) soit assez précis par rapport à ton besoin. Par exemple, dans ton calcul exp, tu avais pris une boucle for qui calculait 10 termes et j'ai vu que ce n'était pas suffisant: le résultat de exp(5) s'écartait de trop de la valeur donnée par le exp du module math. Cela marchait avec 30 mais pour un autre calcul, ce serait peut-être trop ou pas assez: il faut donc savoir quand s'arrêter, et c'est le but de l'erreur donnée à la fonction.

Selon l'objectif poursuivi, tu peux prendre une erreur absolue, par exemple, terme<1E-15 pour le terme calculé, ou une erreur relative à la somme déjà calculée terme/somme<1E-15. De toute façon, les nombres "flottants" normaux comportent au plus environ 17 chiffres significatifs.

Dans une boucle while, tu calcules le terme suivant (à partir du terme précédent!), et tu évalues ce terme par rapport à l'erreur définie:
- si l'erreur est dépassée, tu arrêtes la boucle
- si l'erreur n'est pas dépassée, tu l'ajoutes à la somme et tu continues la boucle while pour le terme suivant.

Pour bien comprendre, ajoute un print dans la boucle while pour voir la décroissance des termes et l'impact sur la précision de la somme.

  • Partager sur Facebook
  • Partager sur Twitter
3 octobre 2012 à 16:20:52

Je n'ai pas compris l'histoire d'erreur et 1E-15
Edit : J'ai essayé ça mais ça ne marche pas
def exp(x,erreur):
    e = 1
    float(e)
    for i in range(erreur):
        e = (e * x)/(i+1)
    return e
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 octobre 2012 à 17:12:43

Au fur et à mesure qu'on calcule les termes de la série, ils ont de moins de moins de contribution à la somme.

Voilà un exemple concret: le calcul de exp(5).

1ère colonne: le numéro du terme calculé
2e colonne: la valeur du terme calculé
3e colonne: la somme des termes jusque et y compris celui-là
4e colonne: le quotient terme/somme

On s'arrête ici au 32e terme, parce que ce quotient est devenu inférieur à 1e-15, limite qu'on s'est donnée au départ: c'est l'erreur relative quotient/somme en dessous de laquelle on arrêtera les calculs.

0    1.000000000000000    1.000000000000000 1.000000e+00
  1    5.000000000000000    6.000000000000000 8.333333e-01
  2   12.500000000000000   18.500000000000000 6.756757e-01
  3   20.833333333333332   39.333333333333329 5.296610e-01
  4   26.041666666666664   65.375000000000000 3.983429e-01
  5   26.041666666666664   91.416666666666657 2.848678e-01
  6   21.701388888888886  113.118055555555543 1.918473e-01
  7   15.500992063492061  128.619047619047592 1.205186e-01
  8    9.688120039682538  138.307167658730123 7.004785e-02
  9    5.382288910934744  143.689456569664856 3.745779e-02
 10    2.691144455467372  146.380601025132222 1.838457e-02
 11    1.223247479757896  147.603848504890124 8.287368e-03
 12    0.509686449899123  148.113534954789259 3.441188e-03
 13    0.196033249961201  148.309568204750462 1.321784e-03
 14    0.070011874986143  148.379580079736598 4.718431e-04
 15    0.023337291662048  148.402917371398644 1.572563e-04
 16    0.007292903644390  148.410210275043028 4.914017e-05
 17    0.002144971660115  148.412355246703129 1.445278e-05
 18    0.000595825461143  148.412951072164276 4.014646e-06
 19    0.000156796173985  148.413107868338273 1.056485e-06
 20    0.000039199043496  148.413147067381772 2.641211e-07
 21    0.000009333105594  148.413156400487367 6.288597e-08
 22    0.000002121160362  148.413158521647716 1.429227e-08
 23    0.000000461121818  148.413158982769545 3.107014e-09
 24    0.000000096067045  148.413159078836600 6.472947e-10
 25    0.000000019213409  148.413159098050016 1.294589e-10
 26    0.000000003694886  148.413159101744895 2.489595e-11
 27    0.000000000684238  148.413159102429120 4.610361e-12
 28    0.000000000122185  148.413159102551305 8.232787e-13
 29    0.000000000021066  148.413159102572365 1.419446e-13
 30    0.000000000003511  148.413159102575889 2.365743e-14
 31    0.000000000000566  148.413159102576458 3.815715e-15
 32    0.000000000000088  148.413159102576543 5.962055e-16

148.413159103


Là où c'est intéressant, c'est qu'on calcule ainsi le nombre exact de termes qu'il faut pour obtenir le bon résultat (ni plus ni moins) grâce au taux d'erreur maxi donné (fixé ici à titre d'exemple à 1e-15):

exp(1): 18 termes suffisent
exp(100): il en faut 189!!!

ok?
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 octobre 2012 à 17:27:24

Si tu veux calculer exp avec beaucoup de précision, tu peux aussi utiliser le module decimal pour avoir des floattants avec une grande précision. Et tu pourras vérifier tes calculs avec Decimal(truc).exp() !
  • Partager sur Facebook
  • Partager sur Twitter
3 octobre 2012 à 17:45:03

Je vais commencer par essayer de résoudre avec la méthode de la suite. J'ai compris la procédure de calcul de exp(5) par contre je ne vois pas comment coder cela.
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 octobre 2012 à 17:59:04

Citation : mercurial17

Je vais commencer par essayer de résoudre avec la méthode de la suite. J'ai compris la procédure de calcul de exp(5) par contre je ne vois pas comment coder cela.



Une petite aide : dans la boucle de la fonction exp, il faut que la fonction "se souvienne" de la valeur de <math>\(x^n\)</math> et de <math>\(n!\)</math>, valeurs qui seront réutilisées pour le tour n + 1...
  • Partager sur Facebook
  • Partager sur Twitter
3 octobre 2012 à 18:11:10

Ah on utilise toujours <math>\(x^n\)</math> et <math>\(n!\)</math> ?
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 octobre 2012 à 18:26:47

Ben tu utilises bien <math>\(e^x = \sum_{n = 0}^{+\infty} \frac{x^n}{n!}\)</math> pour faire ton calcul.

Simplement, il ne faut pas, à chaque tour de boucle, recalculer <math>\(x^n\)</math> et <math>\(n!\)</math> à partir de x et de n mais à partir de <math>\(x^{n - 1}\)</math> et <math>\((n - 1)!\)</math>.
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 octobre 2012 à 18:32:33

Citation : mercurial17

Ah on utilise toujours <math>\(x^n\)</math> et <math>\(n!\)</math> ?



Non! Rappelle toi la formule de récurrence que j'ai donnée plus haut:
- 1er terme d'indice 0 = 1
- terme d'indice n = (terme d'indice (n-1)) * x/n

Imagine que tu connaisses déjà le terme précédent:
=> n = numéro du terme
=> t = terme
=> s = somme du terme

à chaque tour de la boucle while, le terme suivant se calculera comme ça:
=> n = n+1
=> t = t*x/n
=> s = s+t

tu sortiras de la boucle while quand tu auras: t/s < erreur, et le résultat sera donné par s

Et les données de départ:
=> x et erreur=1e-15 passés en argument à la fonction

variables initialisées au début de la fonction:
=> n = 0
=> t = 1
=> s = 1

En faisant ça, je me contente d'appliquer strictement la formule de récurrence!

Tu verras que cette fonction est plus simple que ce que tu as fait au départ, et beaucoup plus rapide.
  • Partager sur Facebook
  • Partager sur Twitter
3 octobre 2012 à 18:43:48

J'ai écrit comme ça mais ça ne marche pas :
def exp(x,erreur):
    n = 0.0
    t = 1.0
    s = 1.0
    while t/s < erreur:
        n = n+1
        t = t*x/n
        s = s+t
    return s

(Je suis sur python 2.7)
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 octobre 2012 à 18:54:21

Tu es vraiment très près du but: tu t'es trompé dans le sens de la condition: while doit continuer tant que t/s est SUPERIEUR à erreur.

[edit] Et change le nom "exp": c'est déjà pris par le module math. Utilise "expo" ou n'importe quoi d'autre.
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 octobre 2012 à 19:04:55

Tu peux terminer le travail en utilisant les subtilités de Python:

n += 1
t *= x/n
s += t


Qui font encore gagner un peu de temps.
  • Partager sur Facebook
  • Partager sur Twitter