Intégrale et la méthode des rectangles - correction#
Approximation du calcul d’une intégrale par la méthode des rectangles.
[1]:
%matplotlib inline
Calcul de l’intégrale#
[2]:
a = -2
b = 3
n = 20
[3]:
import math
f = lambda x: x * math.cos(x)
f(4)
[3]:
-2.6145744834544478
[4]:
def integrale(f, a, b, n):
somme = 0
h = float(b - a) / n
x = a
for i in range(0, n + 1):
somme += f(x) * h
x += h
return somme
# On vérifie ave un cas simple.
integrale(lambda x: x, 0, 1, 10)
[4]:
0.55
[5]:
integrale(f, a, b, n)
[5]:
-2.2320146802585272
Il faut écrire la fonction qui calcule l’intégrale.
Calcul de précision#
On essaye toutes les valeurs de jusqu’à obtenir la précision souhaitée en supposant qu’elle est obtenue à partir du moment où la différence relative entre deux valeurs successives est inférieure à la précision.
[6]:
def integrale_precise(f, a, b, n0, precision):
val = integrale(f, a, b, n0)
val0 = None
while val0 is None or abs(val - val0) / val0 > precision:
val0 = val
n0 += 1
val = integrale(f, a, b, n0)
return val, n0
integrale_precise(lambda x: x, 0, 1, 10, 1e-4)
[6]:
(0.5050000000000003, 100)
Le deuxième nombre indique que la valeur de nécessaire.
[7]:
integrale_precise(f, a, b, n, 1e-4)
[7]:
(-2.2196877891927724, 21)
Calcul plus rapide#
Imaginez un piano. A l’itération , on calcule les touches blanches, à l’itération , les touches noires. A l’itération , on calcule les rectangles de hauteur rouges, puis les verts à puis les à .
[9]:
from IPython.display import Image
Image("int2.png", width=400)
[9]:
[10]:
def integrale_precise_2n(f, a, b, n0, precision):
val = integrale(f, a, b, n0)
val0 = None
h = float(b - a) / n0
while val0 is None or abs(val - val0) / val0 > precision:
val0 = val
n0 *= 2
h /= 2
val = (val + integrale(f, a + h, b, n0)) / 2
return val, n0
integrale_precise_2n(lambda x: x, 0, 1, 10, 1e-4)
[10]:
(0.5000454610560389, 81920)
[11]:
integrale_precise_2n(f, a, b, n, 1e-4)
[11]:
(-2.2169740198498182, 40)
Qu’est-ce qui explique les différences en précision ? Dans le cas de cette intégrale, on veut calculer . A l’itération , on a :
On en déduit que . Autrement dit, l’algorithme s’arrête dès que mais la somme des différences qui resteraient à calculer si on continuait n’est pas négligeable :
Pour cette intégrale, l’algorithme s’arrête alors que le chemin restant à parcourir est aussi grand que la différence entre deux valeurs successives.
La seconde version multiplie le nombre de calculs par deux à chaque fois même si elle évite de recalculer les mêmes valeurs. Une fois qu’on a commencé une série, il faut aller jusqu’au bout. C’est intéressant si la fonction à intégrer est longue à calculer.
L’intégrale converge pour toute fonction réglée ou si l’ensemble de ses discontinuité est un ensemble de mesure nulle. On peut obtenir une autre propriété si on suppose que la fonction est (la dérivée est continue). La dérivée admet un majorant et on s’en sert pour majorer la marge d’erreur.
[13]:
Image("marge.png", width=400)
[13]:
Chaque petit rectangle est de taille et de hauteur maximal où est un majorant de la dérivée. . La marge d’incertitude vérifie :
C’est une suite décroissante et pour obtenir une précision , il suffit de choisir tel que :
Il faut maintenant estimer un majorant pour la dérivée. Comme on calcule la fonction pour un ensemble de points pris dans l’interval , On peut le faire en calculant :
Si , , , .
[14]:
def integrale_precision(f, a, b, n):
somme = 0
h = float(b - a) / n
x = a
max_fp = 0
last_f = 0
for i in range(0, n + 1):
fx = f(x)
somme += f(x) * h
x += h
if last_f is not None:
md = abs(fx - last_f) / h
max_fp = max(max_fp, md)
last_f = fx
return somme, max_fp * n * h**2
def integrale_precise_derivee(f, a, b, n0, precision):
val, prec = integrale_precision(f, a, b, n0)
val0 = None
while val0 is None or prec > precision:
val0 = val
n0 += 1
val, prec = integrale_precision(f, a, b, n0)
return val, n0
integrale_precise_derivee(lambda x: x, 0, 1, 10, 1e-3)
[14]:
(0.5004995004994989, 1001)
[ ]:
La fonction récalcitrante#
L’intégrale de Rienmann converge pour toute fonction dont les discontinuités sont de mesures nulles. Numériquement, c’est autre chose. On peut construire une telle fonction pour laquelle le calcule de l’intégrale ne converge pas.
[15]:
import math
def bizarre(x, n):
if x == 0:
return 1
kn = int(math.log(n) / math.log(2)) + 1
a = 2**kn * x
d = abs(int(a + 1e-10) - a)
if d < 1e-10:
return 2
else:
return 1
bizarre(0.33, 8), bizarre(0.5, 8), bizarre(0.125, 8)
[15]:
(1, 2, 2)
[16]:
def integrale_bizarre(f, a, b, n):
somme = 0
h = float(b - a) / n
x = a
for i in range(0, n + 1):
# même fonction mais on passe n également
somme += f(x, n) * h
x += h
return somme
px = list(range(1, 257))
py = [integrale_bizarre(bizarre, 0, 1, i) for i in px]
[17]:
integrale_bizarre(bizarre, 0, 1, 8)
[17]:
2.125
[18]:
integrale_bizarre(bizarre, 0, 1, 16)
[18]:
2.0625
[19]:
integrale_bizarre(bizarre, 0, 1, 7)
[19]:
1.2857142857142856
[20]:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1)
ax.plot(px, py);
La fonction est bornée, intégrable car elle est constante partout sauf sur un ensemble de mesure nulle qui est plus petit que l’ensemble des rationnels. Pourtant le calcul informatique associée ne converge pas car pour certaines valeurs de , les valeurs prises tombent pour la plupart dans cet ensemble.
Calcul de Monte Carlo#
Ce calcul consiste simplement à tirer des valeurs aléatoirement dans l’intervalle à intégrer puis à en faire la moyenne.
[21]:
import random
def integrale_mc(f, a, b, n):
somme = 0
for i in range(0, n):
x = random.uniform(a, b)
somme += f(x)
return somme / n
# On vérifie ave un cas simple.
integrale_mc(lambda x: x, 0, 1, 100)
[21]:
0.5443922662659958
On peut appeler plusieurs fois la fonction et faire la moyenne des valeurs obtenues pour obtenir un algorithme qui s’arrête à partir d’une certaine précision.
[22]:
def integrale_mc_precise(f, a, b, n0, precision):
val = integrale(f, a, b, n0)
moy = val
moy0 = None
nb = 1
while moy0 is None or abs(moy - moy0) / moy0 > precision:
val += integrale_mc(f, a, b, n0)
nb += 1
moy0 = moy
moy = val / nb
return moy, n0
integrale_mc_precise(lambda x: x, 0, 1, 100, 1e-4)
[22]:
(0.5001005538840249, 100)
Quand s’arrêter ?#
L’intégrale de Monte Carlo est une variable aléatoire qui est une moyenne. où est une variable aléatoire uniforme dans l’intervalle . est une variable aléatoire qui n’est a priori pas uniforme. Elle est bornée si fonction est bornée. En utilisant le théorème centrale limite, on sait que tend vers une loi normale dont on peut majorer la variance par où . Il suffit ensuite de choisir un suffisant grand telle sorte que l’intervalle de confiance à 95% soit suffisamment petit. Si est la précision, et donc .
Pour une précision à :
[ ]:
integrale_mc(lambda x: x, 0, 1, int(16e4))
0.500765653300686
Mesure du temps#
Les temps ne sont pas vraiment comparables puisque les conditions d’arrêt de chaque fonction ne correspondent pas aux même précisions.
[23]:
%timeit integrale_precise(f, a, b, n, 1e-4)
14.4 µs ± 2.18 µs per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
[24]:
%timeit integrale_precise_2n(f, a, b, n, 1e-4)
14 µs ± 221 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
[25]:
%timeit integrale_mc_precise(f, a, b, n, 1e-4)
18.3 µs ± 3.03 µs per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
[26]:
%timeit integrale_precise(lambda x: x, 0, 1, 10, 1e-4)
885 µs ± 156 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
[27]:
%timeit integrale_precise_2n(lambda x: x, 0, 1, 10, 1e-4)
22.4 ms ± 1.31 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
[28]:
%timeit integrale_mc_precise(lambda x: x, 0, 1, 10, 1e-4)
269 µs ± 47 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)