La méthode du “Diviser pour régner” est un paradigme de programmation imaginé pour améliorer la complexité d’un programme. Regardons ce que cela donne en Python.
Le principe du “diviser pour régner” en Python
On souhaite calculer \(N=7^{52}\). La méthode basique consiste à multiplier le nombre 7 par lui-même 52 fois… Ce qui n’est pas très rapide !
L’idée consiste donc à diviser le problème en 2 : on va calculer \( 7^{26} \times 7^{26}\), c’est-à-dire \((7^{26})^2\). Là, il n’y a plus que 26 + 1 opérations (26 multiplications pour calculer \(7^{26}\), et une dernière pour faire le carré du résultat.
On recommence ensuite avec \(7^{26}\) : on le calcule en faisant \( (7^{13})^2 \). \(N\) se calcule alors en 13+1+1 opérations au lieu de 52 initialement.
On ne s’arrête pas là, bien entendu : on continue tant que l’on peut utiliser ce principe :$$\begin{align}N & = 7^{52}\\&= (7^{26})^2\\&= \big((7^{13})^2\big)^2\\&=\big[[(7^6)^2\times7]^2\big]^2\\&=\big[[\big((7^3)^2\big)^2\times7]^2\big]^2\\&=\big[[\big((7^2\times7)^2\big)^2\times7]^2\big]^2 \end{align}$$
Le principe est, vous l’avez peut-être remarqué, récursif.
Implémentation en Python du “diviser pour régner”
Nous allons écrire une fonction “puissance(x,n)” basée sur ce paradigme, où x et n sont deux entiers (positif pour n). Pour cela, nous allons prendre en compte que:$$\begin{cases}x^0 = 0& \\x^n = (x^2)^{n/2} & \text{ si }n\text{ est pair}\\x^n = x(x^2)^{(n-1)/2} & \text{ si }n\text{ est impair} \end{cases}$$Cela donne alors la fonction suivante (exponentiation rapide):
def puissance(x,n): if n == 0: return 1 elif n%2 == 0: return puissance(x*x , n/2) else: return x * puissance(x*x , (n-1)/2) print(puissance(2,9))
Complexité
Notons \(C(n)\) la complexité de la fonction “puissance(n)”. Comme nous divisons en deux le problème à chaque appel de la fonction, on peut estimer que : $$C(n) = a C(n/2) + f(n)$$où \(f(n)\) est la complexité totale due au partage et à la recombinaison, et où:
- \(a=1\) si la fonction s’applique à l’un des deux sous-problèmes;
- \(a=2\) si la fonction s’applique aux deux sous-problèmes.
Dans le cas de l’exponentiation rapide, a = 1 et:
- si n est pair, \(C(n) = C(n/2) + 1\);
- si n est impair, \(C(n) = C\left(\frac{n-1}{2}\right) + 2\).
On arrive à prouver (sans doute au-delà du programme de Terminale NSI) que la complexité de l’exponentiation rapide est \(C(n) = O(\ln n)\).