TP sur les calculs numériques - Mathématiques en PCSI 834 à

publicité
Gaudino, casier 5
version 3.2
TP Méthodes numériques
Lycée Masséna
P.C.S.I.834
On rappelle que l’affichage d’un graphe se fait essentiellement avec la commande plot sous la forme :
plot(listes des abscisses, listes des ordonnées)
Plus précisément : pour tracer le graphe de sinus entre 0 et 10
from math import *
import numpy as np
import matplotlib as mp
import matplotlib.pyplot as plt
#on charge le module math
#et tous les modules pour l'affichage
#et oui
#tout ça
x = np.linspace(0.,10.)
y = [sin(t) for t in x]
#fabrication automatique des abscisses entre 0 et 10
#les ordonnées du graphe, par compréhension par exemple
plt.plot(x,y,'r-')
#le graphe, avec les options : rouge (r) et trait plein (-)
#on aura besoin aujourd'hui de mettre o à la place de - : affichage de gros points
plt.axhline(color='k')
#l'axe x. k pour black...
plt.axvline(color='k')
#l'axe y. k pour black...
plt.show()
#le show affiche le tout (sans cela, rien n'est affiché)
#comment, c'est déjà fini ?
La commande pour effacer un graphe est (avec les conventions d’alias employées) plt.clf().
I.
Résumé sur l’utilisation des fonctions numériques de Python
import
import
import
import
import
import
II.
numpy
scipy
scipy.integrate
scipy.optimize
matplotlib.pyplot
random
#
#
#
#
#
#
Ces deux là amènent aussi un certain
nombre de fonctions mathématiques.
Intégration de fonctions, résolution d'équations différentielles.
Zéros des fonctions.
Pour la génération de graphiques.
Pour faire des probabilités
Mesurer un temps d’éxécution
Pour mesurer un temps d’éxécution, on chargera également le module time. L’instruction time.clock() renvoie à
peu près le temps CPU (en secondes). Ce qui nous intéresse est l’écart entre deux temps, on tapera donc :
import time
t0 = time.clock()
#instructions à chronométrer
duree = time.clock() - t0
III.
Suite récurrente double
1. Programmer le calcul du terme n de la suite récurrente double donnée par :
u0 = u1 = 1
et
∀n ∈ N, un+2 = un+1 + un
On pourra remarquer que ces termes sont des entiers. Comparer avec la formule trouvée en appliquant les
formules vues en cours pour les valeurs jusqu’à n = 100.
2. Programmer le calcul du terme n de la suite récurrente double donnée par :
√
u0 = 1, u1 = 1 − 2
et
∀n ∈ N, un+2 = 2un+1 + un
Comparer avec la formule trouvée en appliquant les formules vues en cours pour les valeurs jusqu’à n = 100.
On doit trouver une grosse différence de comportement. L’expliquer en réfléchissant aux valeurs numériques des
constantes à déterminer dans ces calculs.
1
IV.
Dérivée numérique
Il est fréquent de ne pas pouvoir calculer directement f 0 (x), mais de la remplacer par une valeur approchée, comme
f (x + h) − f (x) f (x) − f (x − h) f (x + h) − f (x − h)
,
,
.
h
h
2h
1. À l’aide de Python, comparer les valeurs des trois méthodes, pour f : x 7→ x3 en x = 1, pour h = 10−k , avec k
de 1 à 20.
2. On doit constater deux phénomènes :
(a) Les dernières valeurs sont fantaisistes. Comprendre pourquoi.
(b) La précision semble s’accroître, puis décroître contre toute attente, avec un optimum vers 10−8 . Votre prof
de TD vous l’expliquera.
V.
Résolution de f (x) = 0
On considère la fonction f :
[0, 4] −→
x
7−→
R
. f semble strictement décroissante, et admettre un unique
cos(x/3) − 1/2
zéro : π.
V.1.
Méthode de Dichotomie
1. Retrouver les formules des points an et bn . Ici, a = 0 et b = 4.
2. Programmer la méthode de dichotomie. Les arguments seront f , a, b, et la précision attendue. La sortie sera un
tuple qui donne la valeur approchée par défaut et le nombre d’étape.
3. Calculer une valeur approchée à 10−5 près. Contrôler le temps d’execution, et le nombre d’étapes données (19
normalement).
4. Comparer la rapidité d’exécution avec celle de la fonction scipy.optimize.bisect. Attention à bien chronométrer les calculs, et pas aussi leurs affichages ! Penser à la précision de cette fonction. On doit constater un ordre
de grandeur semblable.
5. Modifier la méthode de manière à faire afficher sur un même graphe la fonction et les points.
V.2.
Méthode de Newton
1. Retrouver la formule de récurrence un+1 = g(un ). Exprimer en particulier g en fonction de f et f 0 .
2. Programmer la méthode de Newton. Les arguments seront f , sa dérivée calculée à la main, b, et la précision
attendue (au sens de l’écart entre deux valeurs consécutives). La sortie sera un tuple qui donne la valeur approchée
et le nombre d’étape.
3. Calculer une valeur approchée à 10−5 près à partir de 4. Contrôler le temps d’exécution, et le nombre d’étapes
données (le temps d’exécution est similaire, mais regardez la précision !).
4. Tenter de calculer une valeur approchée à partir de 0, 5. Cela illustre le manque d’étude mathématique de la
méthode.
5. Comparer la rapidité d’exécution avec celle de la fonction scipy.optimize.newton. La méthode de Newton de
Python nécessite de lui fournir la dérivée (sinon, Python applique la méthode des sécantes : lire l’aide qu’affiche
Spyder !).
6. Modifier la méthode de manière à faire afficher sur un même graphe la fonction et les points. On doit très bien
voir la rapidité de convergence.
7. Pour ceux qui sont en avance : programmer la méthode des sécantes.
VI.
Résolution d’équations différentielles
VI.1.
Méthode d’Euler
1. Programmer la méthode d’Euler (par exemple l’affichage du graphe) pour le problème :
y0 = y
et
2. Afficher sur un même graphe ce résultat et la fonction exp.
2
y(0) = 1
VI.2.
Limites de la méthode
1. Programmer la méthode d’Euler pour le problème :
y0 = 1 + y2
et
y(0) = 0
Prendre un pas assez modeste (genre 0.1) et un nombre de points faible (genre 20) pour éviter un dépassement
de capacité.
π
2. Afficher sur un même graphe ce résultat et la fonction tan. On programmera au-delà de , pour bien voir le
2
problème.
VI.3.
Comparaison avec les techniques intégrées de Python
Le module scipy.integrate permet directement de réaliser de tels calculs, sans précision sur la méthode employée
(l’intérêt intellectuel de ce paragraphe est donc < ε, et ce pour tout ε > 0, mais c’est au programme).
odeint s’utilise de la manière suivante : elle prend trois arguments au moins sous la forme
4 = scipy.integrate.odeint(1,2,3)
où les chiffres signifient :
1 est la fonction du second membre, qui dépend des deux paramètres y et t dans cet ordre (et ce même si t
n’apparait pas). Utiliser la notation lambda est pratique. Quelques exemples :
(a) y 0 = y 2 : 1 est lambda y,t : y**2
(b) y 0 (t) = 2ty(t) + 1 : 1 est lambda y,t : 2*t*y +1
(c) ty 0 (t) − ln(t)y(t) = cos(t) : 1 est lambda y,t :
cos(t)/t + log(t)*y/t
2 est l’ordonnée initiale du problème de Cauchy (y0 seulement : on parle d’une équation de degré 1).
3 est la liste des abscisses des points en lesquels on approxime. Le mieux est de l’obtenir avec un linspace du
module numpy (qui servira aussi pour le graphe). L’abscisse initiale t0 (0 pour nous) est sa première valeur.
4 (la sortie) est la liste des ordonnées des points (prêts pour un graphe donc, si on le mixe avec la liste des abscisses).
Résoudre les deux exemples de problèmes de Cauchy de degré 1 avec la fonction odeint, partie du module scipy.integrate.
Afficher le graphe de la solution.
VI.4.
Cas d’une équation de degré 2
1. Programmer la méthode d’Euler pour le problème : y 00 + 4y = 0
et
y(0) = 1
et
y 0 (0) = 0. Il est
volontairement un peu différent de celui vu en cours. Il vaut mieux refaire à la main les calculs de vectorialisation
(l’apparition de la matrice).
2. Afficher sur un même graphe ce résultat et la fonction solution (à calculer à la main).
3. Faire de même avec le problème : y 00 + 5y 0 + 4y = 0
VI.5.
et
y(0) = 2
et
y 0 (0) = −5.
Système proies-prédateurs
On considère le système différentiel non linéaire du premier ordre suivant :
0
x (t) = ax(t) − bx(t)y(t)
y 0 (t) = cx(t)y(t) − dy(t)
Ce système modélise l’évolution d’une population de proies et de prédateurs qui interagissent. Dans ce système
– x(t) est l’effectif des proies au temps t,
– y(t) est l’effectif des prédateurs au temps t,
– a est le taux de reproduction des proies en l’absence de prédateurs,
– b est le taux de mortalité des proies due aux prédateurs,
– c est le taux de reproduction des prédateurs en fonction des proies mangées,
– d est le taux de mortalité des prédateurs en l’absence de proies.
Par exemple, cela permet d’étudier la co-évolution d’une population de renards et lapins, ou de requins et sardines 1 .
1. Écrire une fonction Python simul(x0,y0,tmax,n,a,b,c,d): qui calcule par la méthode d’Euler une approximation sur [0, tmax] de la solution de ce système vérifiant la condition initiale (x(0), y(0)) = (x0 , y0 ). Plus
précisément :
1. Historiquement, il s’agissait de poissons dans le port (ou la lagune) de Venise.
3
– x0 et y0 sont les populations initiales au temps t = 0 respectivement des proies et des prédateurs,
– tmax est le temps sur lequel on fait l’approximation (et donc tmax = nh),
– n est le nombre de points calculés dans la méthode d’Euler,
– a,b,c,d sont les paramètres du système.
La fonction simul renverra un triplet formé de la liste des tk , de la liste des approximations xk de x(tk ) et de la
liste des approximations yk de y(tk ), pour 0 ≤ k ≤ n.
2. Tracer la courbe représentant l’évolution des proies, ainsi que celle des prédateurs. On prendra les valeurs
numériques : a = 0.6 ; b = 0.8 ; c = 0.3 ; d = 0.6 ; x0 = 5 et y0 = 1 sur 50 ans avec 5000 points.
3. Tracer la courbe reliant les points (xk , yk ), pour 0 ≤ k ≤ n. Cette courbe représente l’évolution des populations
sur un même graphe (mais on n’y a plus le temps). La bonne manière de la comprendre est de visualiser une
animation : la courbe se dessine au fur et à mesure de l’écoulement des années.
On doit constater qu’elle boucle (à peu près) : le système est globalement stable.
4. Essayer de comprendre intuitivement pourquoi le système est stable. Il faudra réfléchir aux effets de l’augmentation (ou de la diminution) des valeurs de x(t) et y(t).
5. Étudions quelques évolutions numériques, pour comprendre l’écologie de la situation.
(a) Les autres paramètres étant fixes, augmenter progressivement b. Le résultat est-il conforme à l’intuition
(normalement, oui) ? On devrait remarquer quelque-chose vers b = 2, 8.
(b) Les autres paramètres étant fixes, augmenter progressivement le nombre initial de prédateurs. Le résultat
est-il conforme à l’intuition (normalement, oui) ?
(c) On effectue un prélèvement (une chasse ou une pêche) des deux espèces, indistinctement sur les proies et
sur les prédateurs. Mathématiquement, cela revient à retrancher εx(t) à la première équation, et εy(t) à la
deuxième équation, et donc à modifier légèrement certaines valeurs de a, b. . . Afficher sur un même graphe,
avec deux couleurs différentes, la courbe avec et sans prélèvement.
Constater 2 que cette action fait augmenter le nombre de proies et diminuer les prédateurs (essayer avec
des ε du genre 0, 1).
VII.
VII.1.
Calcul approché d’intégrales
À la main (au cerveau, devrait-on dire)
Le but de la séquence est de faire une concurrence déloyale à Bouvart et Ratinet : nous allons dresser une (toute
1
petite) table de logarithmes 3 . On veut donc intégrer la fonction x 7→ de 1 à une autre valeur. Prenons par exemple
x
la valeur 2, pour calculer ln(2).
On fera cette série de questions pour les trois méthodes : rectangles à gauche, trapèzes, Simpson.
∀k ∈ J0, n − 1K ak
Rn (f )
b−a
n
n−1
X
b−a
f (ak )
n
= a+k
=
k=0
Tn (f )
=
σn (f )
=
b−a
n
n−1
X
f (ak ) + f (ak+1 )
2
k=0
n−1 b−a X
ak + ak+1
4f
+ f (ak ) + f (ak+1 )
6n
2
k=0
Z
2
dt
. Le seul argument sera le nombre d’étape n. Les calculs que nous
1 t
faisons aujourd’hui étant très limités, on ne demande pas d’optimiser le code (sauf à la dernière question).
1. Programmer le calcul approché de
2. Comparer la précision avec celle attendue : calculer à la main (ou avec Python) le n nécessaire pour avoir 3
décimales justes (utiliser les estimations d’erreurs vues en cours), et vérifier avec la valeur de ln(2) que connait
Python.
3. Comparer les n des diverses méthodes.
2. Ce n’est pas une preuve, évidemment. La vrai preuve est (bizarement) très simple.
3. Sauf que la méthode choisie est vraiment élémentaire. Heureusement que les auteurs avaient des techniques beaucoup plus intelligentes
(les calculs étaient faits à la main !).
4
4. Optimiser le code pour la méthode des trapèzes : on essaiera de limiter au maximum le nombre de calcul, au
détriment au besoin de la complexité en mémoire. En particulier, on commencera par stocker dans une liste les
1
valeurs de la fonction que nous utiliserons effectivement (les
en les points de la subdivision). Comparer les
x
temps d’éxécution (on doit trouver un facteur 2).
VII.2.
Comparaison avec les techniques intégrées de Python
La méthode d’intégration de scipy où Python utilise une boite noire répugnante la méthode qu’il préfère est
scipy.integrate.quad (il faut se plonger dans l’aide pour savoir ce qu’il fait réellement). Elle renvoie un nombre
complexe (dont la partie imaginaire doit être dans ce cas très petite donc).
La méthode des trapèzes est obtenue dans Python grâce à l’extension scipy, plus précisément grâce à scipy.integrate.trapz.
Celle de Simpson est obtenue par scipy.integrate.simps (étonnant, non ?).
Cela s’utilise ainsi : scipy.integrate.trapz(y,x) où x est la liste des abscisses (données par un linspace par
exemple) et y est la liste des ordonnées correspondantes.
Comparons donc les performances de notre méthode des trapèzes optimisée (avec n = 100 pour avoir un compte rond),
et celle de scipy : si les programmeurs Python ont dépassé le stade Bac+1, ils devraient faire mieux que nous :
1. Programmer la liste des abcisses avec 100 points ;
2. programmer la liste des ordonnées correspondantes ;
3. et faire calculer l’intégrale par ces deux manières, en mesurant le temps d’éxécution.
VII.3.
Méthodes de Monte-Carlo
Une méthode de calcul approché basée sur les probabilités : on importera le module random de Python. On utilisera
la fonction random() qui donne un nombre réel compris entre 0 et 1, en suivant une loi uniforme.
On se donne une fonction f : [a, b] → R. On se donne n points (x1 , ..., xn ) choisis de manière aléatoire dans [a, b]
Z b
n
b−aX
f (xi ) est une approximation de
f , avec un intervalle de confiance (à
(en suivant une loi uniforme). Alors
n i=1
a
√
95%) de largeur α/ n.
1. Écrire une fonction qui prend en argument f , a, b, n et rend l’approximation de l’intégrale par la méthode de
Monte-Carlo en n points. Tester sur différentes fonctions et plusieurs valeurs de n.
2. Adapter la méthode pour obtenir une approximation de π. On considère pour cela le carré C = [0, 1] × [0, 1] qui
contient le quart de disque D centré en 0 et de rayon 1. On choisit de manière aléatoire un point dans C et on
teste son appartenance à D. Si on note N le nombre total de points et ND le nombre de points dans le quart
de √
disque, le rapport ND /N donne une approximation de π/4 avec un intervalle de confiance à 95% de largeur
α/ n.
Programmer la méthode. Tester la qualité de l’approximation.
5
Téléchargement