1Dichotomie et methode de Newton
Etant donne \(f : [a,b] \to \mathbb R\), trouver \(\ell\) tel que \(f(\ell) = 0\).
Toute equation \(g(x) = h(x)\) se ramene a ce probleme en posant \(f = g - h\). Or la plupart des equations n'ont pas de solution exprimable : \(\cos x = x\), \(xe^x = 1\), \(x^5 - x - 1 = 0\) (theoreme d'Abel : pas de radicaux au-dela du degre 4). Le calcul approche est donc la regle, pas l'exception.
Si \(f\) est continue sur \([a,b]\) et si \(f(a)f(b) < 0\), alors il existe \(\ell \in\;]a,b[\) tel que \(f(\ell) = 0\). Si de plus \(f\) est strictement monotone, ce zero est unique.
C'est ce theoreme qui fonde la methode de dichotomie : il garantit qu'un changement de signe enferme une racine.
Methode de dichotomie
On maintient un intervalle \([a,b]\) contenant une racine (signes opposes aux bornes). A chaque etape, on evalue \(f\) au milieu \(m\) et l'on conserve la moitie ou le changement de signe persiste. La longueur de l'intervalle est divisee par 2 a chaque iteration.
def dichotomie(f, a, b, eps=1e-12, nmax=200):
"""Zero de f dans [a,b]. Precondition : f continue et f(a)*f(b) < 0.
Renvoie (racine, nombre d'iterations).
"""
fa, fb = f(a), f(b)
if fa == 0: return a, 0
if fb == 0: return b, 0
if fa * fb > 0:
raise ValueError("f(a) et f(b) doivent etre de signes opposes")
n = 0
while b - a > eps and n < nmax:
m = (a + b) / 2
fm = f(m)
if fm == 0:
return m, n
if fa * fm < 0:
b = m # le zero est dans [a, m]
else:
a, fa = m, fm # il est dans [m, b]
n += 1
return (a + b) / 2, n
from math import cos
r, n = dichotomie(lambda x: cos(x) - x, 0, 1)
print(f"{r:.15f} en {n} iterations") # 0.739085133215161 en 40 iterations
Terminaison et vitesse : apres \(n\) iterations, \(b_n - a_n = \dfrac{b_0-a_0}{2^n}\), donc \[ |x_n - \ell| \le \frac{b_0-a_0}{2^{n+1}}, \qquad n \ge \log_2\!\left(\frac{b_0-a_0}{\varepsilon}\right). \] Pour \(b_0-a_0 = 1\) et \(\varepsilon = 10^{-15}\) : 50 iterations. La convergence est lineaire de rapport \(1/2\) : on gagne 1 bit, soit environ 0,3 decimale, par iteration.
Invariant : « \(f(a)f(b) < 0\), donc \([a,b]\) contient un zero de \(f\) ». Il est preserve par construction, et le variant \(b - a\) tend vers 0 : la methode est totalement correcte.
| Avantages | Inconvenients |
|---|---|
| convergence garantie sous ses hypotheses | lente (lineaire) |
| ne demande que la continuite | exige un changement de signe (rate les racines doubles) |
| erreur majoree a priori | ne se generalise pas a la dimension superieure |
| insensible aux pathologies de \(f'\) | n'exploite pas la regularite de \(f\) |
Methode de Newton
On remplace \(f\) par sa tangente au point courant, et l'on prend pour nouvelle approximation le zero de cette tangente. La tangente en \(x_n\) a pour equation \(y = f(x_n) + f'(x_n)(x - x_n)\), qui s'annule en
\[ \boxed{\;x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)}.\;} \]
def newton(f, fp, x0, eps=1e-14, nmax=100):
"""Zero de f par la methode de Newton. fp est la derivee.
Renvoie (racine, nombre d'iterations, convergence).
"""
x = x0
for n in range(1, nmax + 1):
d = fp(x)
if d == 0:
return x, n, False # tangente horizontale : echec
x_nouveau = x - f(x) / d
if abs(x_nouveau - x) < eps * max(1.0, abs(x_nouveau)):
return x_nouveau, n, True
x = x_nouveau
return x, nmax, False
from math import cos, sin
r, n, ok = newton(lambda x: cos(x) - x, lambda x: -sin(x) - 1, 1.0)
print(f"{r:.15f} en {n} iterations") # 0.739085133215161 en 5 iterations
Soit \(\ell\) un zero simple de \(f\) (\(f'(\ell) \ne 0\)), \(f\) de classe \(\mathcal C^2\) au voisinage de \(\ell\). Alors il existe \(\delta > 0\) tel que, pour tout \(x_0 \in [\ell-\delta, \ell+\delta]\), la suite de Newton converge vers \(\ell\) et \[ |x_{n+1} - \ell| \;\le\; K\,|x_n - \ell|^2, \qquad K = \frac{\max|f''|}{2\min|f'|}. \]
La formule de Taylor-Lagrange a l'ordre 2 entre \(x_n\) et \(\ell\) donne l'existence de \(\xi\) entre les deux tels que \[ 0 = f(\ell) = f(x_n) + (\ell - x_n)f'(x_n) + \frac{(\ell-x_n)^2}{2}f''(\xi). \] En divisant par \(f'(x_n)\) (non nul au voisinage de \(\ell\)) : \[ 0 = \frac{f(x_n)}{f'(x_n)} + \ell - x_n + \frac{(\ell-x_n)^2}{2}\frac{f''(\xi)}{f'(x_n)}. \] Or \(x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)}\), donc \[ \ell - x_{n+1} = -\frac{(\ell-x_n)^2}{2}\frac{f''(\xi)}{f'(x_n)}, \] d'ou \(|x_{n+1}-\ell| \le K|x_n-\ell|^2\) en majorant.
Interpretation : le nombre de decimales exactes double a chaque iteration. Partant de 1 decimale : 2, 4, 8, 16. Cinq iterations suffisent pour atteindre la precision machine.
from math import atan
# 1. divergence : le point de depart est trop loin
r, n, ok = newton(atan, lambda x: 1/(1+x*x), 2.0)
print(ok) # False : la suite oscille en s'eloignant
# 2. tangente horizontale
newton(lambda x: x**3 - 2*x + 2, lambda x: 3*x*x - 2, 0.0) # cycle 0 -> 1 -> 0
# 3. racine double : convergence seulement lineaire
newton(lambda x: (x-1)**2, lambda x: 2*(x-1), 2.0)
Trois modes d'echec : divergence si \(x_0\) est mal choisi, division par zero si \(f'(x_n) = 0\), et perte de la convergence quadratique sur une racine multiple.
2Visualisation, secante et comparaison
Methode de la secante
Newton exige de connaitre \(f'\). Quand elle est inconnue ou couteuse, on la remplace par le taux d'accroissement entre les deux derniers points :
\[ x_{n+1} = x_n - f(x_n)\,\frac{x_n - x_{n-1}}{f(x_n) - f(x_{n-1})}. \]
def secante(f, x0, x1, eps=1e-14, nmax=100):
"""Newton sans derivee. Ordre de convergence : le nombre d'or ~ 1.618."""
f0, f1 = f(x0), f(x1)
for n in range(1, nmax + 1):
if f1 == f0:
return x1, n, False
x2 = x1 - f1 * (x1 - x0) / (f1 - f0)
if abs(x2 - x1) < eps * max(1.0, abs(x2)):
return x2, n, True
x0, f0, x1, f1 = x1, f1, x2, f(x2)
return x1, nmax, False
L'erreur verifie \(e_{n+1} \approx K e_n e_{n-1}\). En cherchant \(e_n \approx A e_{n-1}^\alpha\), on obtient \(\alpha^2 = \alpha + 1\), donc \(\alpha = \varphi = \frac{1+\sqrt5}{2} \approx 1{,}618\) : c'est le nombre d'or.
Moins bien que Newton par iteration, mais la secante n'evalue \(f\) qu'une fois par etape contre deux (\(f\) et \(f'\)) pour Newton. A cout egal, son ordre effectif est \(\varphi^2 \approx 2{,}6 > 2\) : elle est souvent plus efficace en pratique.
Comparaison
| Dichotomie | Newton | Secante | |
|---|---|---|---|
| Ordre | 1 (rapport 1/2) | 2 | 1,618 |
| Convergence garantie | oui | non (locale) | non |
| Hypotheses | continuite, changement de signe | \(\mathcal C^2\), \(f'(\ell) \ne 0\), \(x_0\) proche | deux points initiaux |
| Evaluations par iteration | 1 (\(f\)) | 2 (\(f\) et \(f'\)) | 1 (\(f\)) |
| Iterations pour \(10^{-15}\) | ~50 | ~5 | ~7 |
| Generalisation a \(\mathbb R^n\) | non | oui (jacobienne) | oui (quasi-Newton) |
3Methodes hybrides, criteres d'arret et applications
Methodes hybrides
On combine la surete de la dichotomie et la vitesse de Newton : on tente un pas de Newton ; s'il sort de l'intervalle d'encadrement ou ne reduit pas assez l'erreur, on effectue un pas de dichotomie a la place.
def hybride(f, fp, a, b, eps=1e-14, nmax=100):
"""Newton securise par dichotomie. Convergence garantie ET rapide."""
fa, fb = f(a), f(b)
if fa * fb > 0:
raise ValueError("pas de changement de signe")
x = (a + b) / 2
for n in range(1, nmax + 1):
d = fp(x)
pas_newton = (d != 0)
if pas_newton:
xn = x - f(x) / d
pas_newton = (a < xn < b) # le pas reste-t-il dans l'encadrement ?
x = xn if pas_newton else (a + b) / 2 # sinon : dichotomie
fx = f(x)
if fx == 0 or (b - a) < eps:
return x, n, "Newton" if pas_newton else "dichotomie"
if fa * fx < 0:
b = x
else:
a, fa = x, fx
return x, nmax, "max atteint"
from math import atan
# atan fait diverger Newton depuis x0 = 2, mais l'hybride converge
print(hybride(atan, lambda x: 1/(1+x*x), -6, 6))
C'est le principe de la methode de Brent, utilisee par
scipy.optimize.brentq : elle combine dichotomie, secante et interpolation quadratique
inverse, avec convergence garantie et un ordre pratique proche de 1,8.
Choix du critere d'arret
while abs(f(x)) > eps: # 1. residu : trompeur si |f'| est tres petit
while abs(x1 - x0) > eps: # 2. increment absolu : mal adapte aux grandes valeurs
while abs(x1 - x0) > eps * abs(x1): # 3. increment relatif : le plus robuste
- Le residu \(|f(x)|\) peut etre minuscule loin de la racine si \(f\) est tres plate : sur \(f(x) = (x-1)^7\), \(|f(1{,}1)| = 10^{-7}\) alors que l'erreur vaut \(0{,}1\).
- L'increment absolu ne convient pas si la racine vaut \(10^{10}\) : demander \(10^{-15}\) d'increment absolu est impossible en double precision.
- La bonne pratique combine les deux, avec un nombre maximal d'iterations pour garantir la terminaison.
def critere(x_nouveau, x, f_x, eps=1e-14, tol=1e-14):
return (abs(x_nouveau - x) < eps * max(1.0, abs(x_nouveau))
or abs(f_x) < tol)Applications classiques
**
Pour calculer \(\sqrt[k]{A}\), on annule \(f(x) = x^k - A\). Newton donne
\[ x_{n+1} = x_n - \frac{x_n^k - A}{k x_n^{k-1}} = \frac{1}{k}\left((k-1)x_n + \frac{A}{x_n^{k-1}}\right). \]
def racine_kieme(A, k, eps=1e-15):
"""Racine k-ieme de A > 0 par Newton (methode de Heron generalisee)."""
x = A if A > 1 else 1.0
for _ in range(100):
x_nouveau = ((k-1)*x + A / x**(k-1)) / k
if abs(x_nouveau - x) < eps * x:
return x_nouveau
x = x_nouveau
return x
assert abs(racine_kieme(2, 2) - 2**0.5) < 1e-14
assert abs(racine_kieme(1000, 3) - 10) < 1e-12
Pour \(k = 2\) on retrouve la methode de Heron \(x_{n+1} = \frac12(x_n + A/x_n)\), connue des Babyloniens vers 1700 av. J.-C.
Pour calculer \(1/A\) sans diviser, on annule \(f(x) = 1/x - A\), d'ou \(f'(x) = -1/x^2\) et
\[ x_{n+1} = x_n - \frac{1/x_n - A}{-1/x_n^2} = x_n(2 - Ax_n). \]
def inverse(A, x0=None, n=30):
"""1/A par Newton, sans aucune division. Convergence si 0 < x0 < 2/A."""
x = x0 if x0 is not None else 2.0**(-A.bit_length()) if isinstance(A, int) else 0.1
for _ in range(n):
x = x * (2 - A*x)
return x
print(inverse(7, 0.1), 1/7) # 0.14285714285714285 0.14285714285714285
Cette iteration est reellement utilisee dans les processeurs : la division materielle etant lente, on calcule l'inverse par quelques iterations de Newton a partir d'une valeur initiale lue dans une table, puis on multiplie.
Resoudre \(f(x) = 0\) equivaut souvent a resoudre \(g(x) = x\) pour un \(g\) bien choisi.
def point_fixe(g, x0, eps=1e-14, nmax=1000):
"""Iteration x -> g(x). Converge si g est contractante au voisinage du point fixe."""
x = x0
for n in range(1, nmax+1):
y = g(x)
if abs(y - x) < eps * max(1.0, abs(y)):
return y, n, True
x = y
return x, nmax, False
from math import cos, sqrt
print(point_fixe(cos, 1.0)) # 0.739085..., ~85 iterations
print(point_fixe(lambda x: sqrt(2 + x), 0.0)) # 2.0, ~30 iterations
Theoreme du point fixe de Banach : si \(g\) est \(k\)-lipschitzienne avec \(k < 1\) sur un intervalle stable \(I\) (c'est-a-dire \(g(I) \subset I\)), alors \(g\) admet un unique point fixe \(\ell\) dans \(I\), la suite \(x_{n+1} = g(x_n)\) converge vers \(\ell\) pour tout \(x_0 \in I\), et \[ |x_n - \ell| \le \frac{k^n}{1-k}|x_1 - x_0|. \] La convergence est lineaire de rapport \(k = |g'(\ell)|\). Newton est en fait un point fixe pour \(g(x) = x - f(x)/f'(x)\), dont on verifie que \(g'(\ell) = 0\) : c'est ce qui donne l'ordre 2.
L'essentiel du chapitre
- Dichotomie : sure, lente (lineaire, rapport 1/2), exige un changement de signe. Erreur majoree a priori par \((b-a)/2^{n+1}\).
- Newton : \(x_{n+1} = x_n - f(x_n)/f'(x_n)\), quadratique mais convergence seulement locale. Le nombre de decimales double a chaque etape.
- Secante : ordre \(\varphi \approx 1{,}618\), sans derivee, souvent la plus efficace a cout egal.
- Newton echoue sur les racines multiples (convergence lineaire), les tangentes horizontales et les mauvais points de depart.
- Les methodes hybrides (Brent) combinent surete et vitesse.
- Critere d'arret : increment relatif, et toujours un nombre maximal d'iterations.