Dans cette partie nous allons donc traiter le cas des algorithmes récursifs, et voir comment le calcul de leur complexité se déduit d'une étude de suite 
Dans un algorithme itératif, le décompte du nombre d'opérations élémentaires se fait en additionnant le nombre d'opérations de chaque itération. C'est pourquoi dans le chapitre précédent nous avons souvent utilisé des calculs de sommes 
La complexité d’un algorithme récursif traitant des données d'une certaine taille va quand à elle naturellement dépendre de la complexité du même algorithme avec des données de taille inférieure. Cette complexité va donc être une suite définie par récurrence 
Définition 
Soit k k N ∗ ℕ∗ ( u n ) n ∈ N (un)n∈ℕ 
On dit que la suite ( u n ) n ∈ N (un)n∈ℕ k k f f R k ℝk R ℝ 
u n = f ( u n − 1 , u n − 2 , . . . , u n − k ) un=f(un−1,un−2,...,un−k) 
 
Dans la suite de cette partie on se limitera au cas des suites arithmético-géométriques récurrentes linéaires d’ordre 2 2 
 
  
 
 
 
Commençons par une définition, sans doute déjà connue du lecteur.
Définition 
Une suite réelle ( u n ) n ∈ N (un)n∈ℕ arithmético-géométrique a a b b 
∀ n ∈ N ∗ , u n = a u n − 1 + b ∀n∈ℕ*, un=aun−1+b 
 
On prend l'habitude de distinguer deux cas particuliers, celui des suites arithmétiques géométriques 
Définition 
Avec les notations de la définition précédente :
Si a = 1 a=1 ( u n ) n ∈ N (un)n∈ℕ arithmétique b b 
 
Si b = 0 b=0 ( u n ) n ∈ N (un)n∈ℕ géométrique a a 
 
 
 
 
Le résultat suivant nous donne l'expression explicite 
Propriété 
Soit ( u n ) n ∈ N (un)n∈ℕ a a b b 
Si a = 1 a=1 
∀ n ∈ N , u n = u 0 + n b ∀n∈ℕ,  un=u0+nb 
Si a ≠ 1 a≠1 l = b 1 − a l=b1-a 
∀ n ∈ N , u n = ( u 0 − l ) a n + l ∀n∈ℕ,  un=(u0−l)an+l 
 
 
On admettra ce résultat, ce n’est pas l’objet de ce cours. Ce n’est ceci-dit pas bien méchant à démontrer.
 
 
 
 
Nous utiliserons peu cette expression exacte, mais plutôt l'approximation suivante qui nous renseigne sur l'ordre de grandeur 
Propriété 
Soit ( u n ) n ∈ N (un)n∈ℕ a a b b 
On suppose que a a b b u 0 u0 
Si a = 1 a=1 u n = Θ ( n ) un=Θ(n) 
Si a > 1 a>1 u n = Θ ( a n ) un=Θ(an) 
 
 
La restriction que a a b b u 0 u0 
 
 
 
 
Voyons maintenant sur des exemples comment appliquer ce qui précède. Le premier concerne la très fameuse fonction factorielle.
Example 1.1. Complexité de la fonction factorielle récursive 
Rappelons tout d'abord que n ! = 1 × 2 × 3 × ... × n n!=1×2×3×...×n n ! = n × ( n − 1 ) ! n!=n×(n-1)! 
L'implémentation en Python de cette fonction est :
def  factorielleRecursive (n) :if  n == 0 :
        return  1 
    else :
        return  n*factorielleRecursive(n-1 )Cette fonction n'est composée que d'une structure conditionelle.
Si n > 0 n>0 T ( n ) = T ( n − 1 ) + 2 T(n)=T(n-1)+2 
Si n = 0 n=0 T ( 0 ) = 1 T(0)=1 
La complexité T T a = 1 a=1 b = 2 b=2 
D'après les résultats précédents, on a alors T ( n ) = 1 + 2 n T(n)=1+2n T ( n ) = Θ ( n ) T(n)=Θ(n) 
 
 
A noter que l'algorithme itératif calculant cette factorielle a la même complexité 
def  factorielleIterative (n) :1 
    for  i in  range(2 ,n+1 ):
        resultat *= i
    return  resultatOn a une structure for n − 1 n-1 T ( n ) = Θ ( n ) T(n)=Θ(n) 
 
 
 
 
L'exemple suivant est le fameux problème des tours de Hanoï Edouard Lucas 
 
Ce problème a été recontré dans les séances d'exercices du cours 1ADS  mais nous allons le redétailler.
 
 
 
 
Example 1.2. Problème des tours de Hanoï 
Le but est de déplacer n n 
Initialement, tous les disques sont empilés sur la tour initiale, du plus grand au plus petit.
Les règles de déplacement sont les suivantes :
On ne peut bouger qu’un seul disque à la fois.
 
On ne peut déplacer un disque que vers une tour vide, ou sur un disque plus grand.
 
 
 
Etudions "à la main" le cas n = 3 n=3 
Voici la position initiale :
Effectuons ces trois premiers mouvements :
On a donc réussi à bouger les deux plus petits disques vers la tour intermédiaire en passant par la tour finale.
Bougeons maintenant le plus grand :
Il ne reste plus qu'à effectuer ces trois derniers mouvements qui ressemblent évidemment fortement aux trois premiers :
Et le problème est résolu pour trois disques.
Essayons maintenant de reproduire dans le cas général ce que l'on vient de faire dans ce cas très particulier. On arrive vite à la conclusion que la résolution du problème pour toute valeur de n n 
On déplace n − 1 n-1 
 
On déplace le plus grand disque de la tour initiale vers la tour finale.
 
On déplace n − 1 n-1 
 
 
 
Le cas de base étant le déplacement d'un seul disque.
En Python cela donne cette implémentation :
def  hanoi (de,vers,via,n) :if  n==1 :
        print(de," vers " ,vers)
    else :
        hanoi(de,via,vers,n-1 )
        print(de," vers " ,vers)
        hanoi(via,vers,de,n-1 )
hanoi("1" ,"3" ,"2" ,3 )Sans surprises, pour trois disques on retrouve bien les mouvements précédemment mis en images :
1  vers  3
1  vers  2
3  vers  2
1  vers  3
2  vers  1
2  vers  3
1  vers  3 
Venons-en maintenant au calcul de la complexité de cette procédure qui n'est composée que d'une structure conditionnelle.
Si n > 1 n>1 T ( n ) = 2 T ( n − 1 ) + 2 T(n)=2T(n-1)+2 
Si n = 1 n=1 T ( 1 ) = 1 T(1)=1 
La complexité T T a = 2 a=2 b = 2 b=2 
D’après le résultat précédent, on a alors T ( n ) = Θ ( 2 n ) T(n)=Θ(2n) 
 
 
  
 
 
 
Cette sous-partie n'est pas conceptuellement très difficile, mais elle demande de la vigilance car certains résultats sont assez techniques.
On distinguera le cas des suites homogènes suites non homogènes 
Commençons naturellement par une définition du concept.
Définition 
Une suite réelle ( u n ) n ∈ N (un)n∈ℕ suite récurrente linéaire homogène d'ordre deux a a b b 
∀ n ≥ 2 , u n = a u n − 1 + b u n − 2 ∀n≥2,  un=aun−1+bun−2 
 
Pour étudier une telle suite, on doit tout d'abord lui associer un polynôme dit caractéristique 
Définition 
Soit ( u n ) n ∈ N (un)n∈ℕ a a b b 
Le polynôme caractéristique ( u n ) n ∈ N (un)n∈ℕ x 2 − a x − b x2-ax-b 
L'équation caractéristique ( u n ) n ∈ N (un)n∈ℕ x 2 − a x − b = 0 x2-ax-b=0 
 
Le terme général racines polynôme caractéristique 
 
On ne fera pas l'insulte au lecteur de lui rappeler comment l'on calcule les racines d'un polynôme du second degré.
 
 
 
 
Propriété 
Soit ( u n ) n ∈ N (un)n∈ℕ Δ Δ 
Si Δ > 0 Δ>0 x 1 x1 x 2 x2 
∃ λ , μ ∈ R , ∀ n ∈ N , u n = λ x n 1 + μ x n 2 ∃λ,μ∈ℝ, ∀n∈ℕ, un=λx1n+μx2n 
Si Δ = 0 Δ=0 x x 
∃ λ , μ ∈ R , ∀ n ∈ N , u n = ( λ n + μ ) x n ∃λ,μ∈ℝ, ∀n∈ℕ, un=(λn+μ)xn 
Dans les deux cas, les constantes λ λ μ μ 
 
 
On ne traitera pas le cas où Δ < 0 Δ<0 
En un mot, on peut alors montrer que les suites sont alors exprimables à l’aide des fonctions cosinus sinus exponentielle complexe 
 
 
 
 
Appliquons ce resultat sur un exemple célèbre, la suite due au mathématicien italien Leonardo Fibonacci 
Example 1.3. Suite de Fibonacci 
La suite de Fibonnaci est la suite récurrente linéaire homogène d'ordre deux définie par
u n = ⎧ ⎪ ⎨ ⎪ ⎩ 0 si  n = 0 1 si  n = 1 u n − 1 + u n − 2 si  n ≥ 2 un={0si n=01si n=1un−1+un−2si n≥2 
L'équation caractéristique de cette suite est x 2 − x − 1 = 0 x2-x-1=0 Δ Δ 5 5 x 1 = 1 + √ 5 2 x1=1+52 x 2 = 1 − √ 5 2 x2=1-52 
D'après le résultat précédent
∃ λ , μ ∈ R , ∀ n ∈ N , u n = λ ( 1 + √ 5 2 ) n + μ ( 1 − √ 5 2 ) n ∃λ,μ∈ℝ, ∀n∈ℕ, un=λ(1+52)n+μ(1−52)n 
On détermine alors λ λ μ μ u 0 u0 u 1 u1 
On a en effet d'une part u 0 = 0 u0=0 u 1 = 1 u1=1 
⎧ ⎨ ⎩ u 0 = λ + μ u 1 = λ ( 1 + √ 5 2 ) + μ ( 1 − √ 5 2 ) {u0=λ+μu1=λ(1+52)+μ(1−52) 
On doit donc résoudre le système
⎧ ⎨ ⎩ 0 = λ + μ 1 = λ ( 1 + √ 5 2 ) + μ ( 1 − √ 5 2 ) {0=λ+μ1=λ(1+52)+μ(1−52) 
On trouve λ = 1 √ 5 λ=15 μ = − 1 √ 5 μ=-15 
∀ n ∈ N , u n = 1 √ 5 ( 1 + √ 5 2 ) n − 1 √ 5 ( 1 − √ 5 2 ) n ∀n∈ℕ, un=15(1+52)n−15(1−52)n 
 
Rappelons maintenant un résultat classique sur les limites, qui nous permettra ensuite d'obtenir l'ordre de grandeur d'une suite récurrente linéaire.
Propriété 
Soit x x R ℝ 
On a
Si x > 1 x>1 lim n → ∞ x n = + ∞ limn→∞xn=+∞ 
 
Si − 1 < x < 1 -1<x<1 lim n → ∞ x n = 0 limn→∞xn=0 
 
Si x ≤ − 1 x≤-1 x n xn n → + ∞ n→+∞ 
 
 
 
 
Le prochain résultat sera celui qui in fine nous servira lors de nos calculs de complexité.
Propriété 
Soit ( u n ) n ∈ N (un)n∈ℕ Δ Δ 
On suppose que Δ > 0 Δ>0 x 1 x1 x 2 x2 
Si x 1 > 1 x1>1 | x 1 | > | x 2 | |x1|>|x2| u n = Θ ( x n 1 ) un=Θ(x1n) 
 
Cette propriété signifie moralement que sous certaines conditions x n 1 x1n 
Example 1.4. Ordre de grandeur de la suite de Fibonacci 
Pour la suite de Fibonacci, on a montré que Δ = 5 Δ=5 x 1 = 1 + √ 5 2 x1=1+52 x 2 = 1 − √ 5 2 x2=1-52 u n = Θ ( ( 1 + √ 5 2 ) n ) un=Θ((1+52)n) 
 
 
La différence avec une suite homogène réside dans la présence d'un terme supplémentaire dans l'équation de récurrence.
Définition 
Une suite réelle ( v n ) n ∈ N (vn)n∈ℕ suite récurrente linéaire non homogène d'ordre deux a a b b f f N ℕ R ℝ 
∀ n ≥ 2 , v n = a v n − 1 + b v n − 2 + f ( n ) ∀n≥2, vn=avn−1+bvn−2+f(n) 
 
On pourrait donner des résultats très précis sur le terme général d'une telle suite, mais nous nous contenterons de la propriété suivante qui en donne son ordre de grandeur. En un mot, pour étudier une suite non homogène il convient d'étudier la suite homogène associée, i.e. f f 
Propriété 
Soit ( v n ) n ∈ N (vn)n∈ℕ non homogène a a b b f f 
On considère ( u n ) n ∈ N (un)n∈ℕ homogène a a b b Δ Δ Δ > 0 Δ>0 x 1 x1 x 2 x2 
Si x 1 > 1 x1>1 | x 1 | > | x 2 | |x1|>|x2| f f v n = Θ ( x n 1 ) vn=Θ(x1n) 
 
Moralement cela signifie que si elle est polynomiale, la fonction f f 
Terminons cette partie par un exemple, avec l'implémentation de la suite de Fibonacci et le calcul de sa complexité.
Example 1.5. Complexité de la fonction récursive calculant le terme général de la suite de Fibonacci 
Voici une fonction récursive calculant le nième terme de la suite de Finonacci :
def  fibonacci (n) :if  n<2 :
        return  n
    else :
        return  fibonacci(n-1 ) + fibonacci(n-2 )Si n > 2 n>2 T ( n ) = T ( n − 1 ) + T ( n − 2 ) + 2 T(n)=T(n-1)+T(n-2)+2 
Si n = 0 n=0 n = 1 n=1 T ( 0 ) = T ( 1 ) = 1 T(0)=T(1)=1 
Dans cet exemple la complexité est donc une suite récurrente linéaire non homogène d'ordre deux.
La suite homogène associée est
u n = ⎧ ⎪ ⎨ ⎪ ⎩ 1 si  n = 0 1 si  n = 1 u n − 1 + u n − 2 si  n ≥ 2 un={1si n=01si n=1un−1+un−2si n≥2 
Cette dernière suite est quasiment celle de Fibonacci, seul le premier terme diffère. La relation de récurrence est en tout cas la même que pour la suite de Fibonacci donc (avec les notations précédentes) Δ = 5 Δ=5 x 1 = 1 + √ 5 2 x1=1+52 x 2 = 1 − √ 5 2 x2=1-52 
D'autre part, la fonction f f 2 2 
Toute les hypothèses de la propriété précédente sont vérifiées, ainsi T ( n ) = Θ ( ( 1 + √ 5 2 ) n ) T(n)=Θ((1+52)n) 
 
 
 
 
  
 
 
 
Après les récursivités relativement simples de la partie précédente, nous allons maintenant nous confronter à celles inhérentes à l'utilisation d'une technique de type "diviser pour régner 
Soyons honnête, ce qui va suivre est délicat et demandera de gros efforts au lecteur.
Commençons par rappeler les trois étapes du paradigme "diviser pour régner 
Diviser plusieurs sous-parties 
 
Régner résout récursivement 
 
Combiner combine 
 
 
 
Avec ce paradigme, la complexité d’un algorithme traitant des données de taille n n n / b n/b b b 
Il va falloir de plus tenir compte de la complexité de l’étape de combinaison 
Avant le cas général, nous allons commencer par étudier un exemple, celui du tri fusion 
 
Le tri fusion 
 
Nous renvoyons le lecteur à ce chapitre  du cours 1ADS pour la description des principaux algorithmes de tri, y compris celui-ci.
 
 
 
 
Voici donc les trois étapes 
Diviser la liste en deux sous-listes de même taille (à un élément près) en la "coupant" par la moitié.
 
Trier récursivement chacune de ces deux sous-listes. Arrêter la récursion lorsque les listes n'ont plus qu'un seul élément.
 
Fusionner les deux sous-listes triées en une seule.
 
 
 
Example 1.6. Déroulement du tri fusion sur un exemple 
On considère la liste suivante de sept entiers :
On la scinde en deux sous-listes en la coupant par la moitié :
Sous-listes que l’on scinde à leur tour :
Sous-listes que l’on scinde à leur tour :
Ces sous-listes sont triées car elles n’ont qu’un élément. On va maintenant les fusionner deux par deux en de nouvelles sous-listes triées :
De nouveau une étape de fusionnnement :
Une dernière fusion :
On a fusionné toutes les sous-listes obtenues lors des appels récursifs, le tri est terminé :
 
En voici son implémentation en Python 
def  fusion (l,first,middle,last) :1 
    pro = []
    while  (i<=middle) and  (j<=last):
        if  l[i]<=l[j]:
            pro.append(l[i])
            i+=1 
        else :
            pro.append(l[j])
            j+=1 
    while  i<=middle:
        pro.append(l[i])
        i+=1 
    while  j<=last:
        pro.append(l[j])
        j+=1 
    for  k in  range(last-first+1 ):
        l[first+k] = pro[k]
def  triFusionRec (l,first,last) :if  first<last:
        triFusionRec(l,first,(first+last)//2 )
        triFusionRec(l,((first+last)//2 )+1 ,last)
        fusion(l,first,(first+last)//2 ,last)
def  triFusion (l) :0 ,len(l)-1 )
 
Bien prendre le temps d'appréhender le fonctionnement de cet algorithme.
 
 
 
 
Voici maintenant le résultat établissant la complexité du tri fusion. Vu la nature même de celui-ci, le lecteur comprendra facilement qu'il n'y a pas de meilleur ou pire des cas.
Théorème 
La complexité du tri fusion est T ( n ) = Θ ( n l o g 2 ( n ) ) T(n)=Θ(nlog2(n)) 
Il s'agit de la meilleure 
 
L'idée pour démontrer ce résultat est de construire l'arbre des appels récursifs 
Démonstration 
Commençons par remarquer que la procédure de fusion complexité linéaire n / 2 n/2 Θ ( n ) Θ(n) n n n / 2 n/2 
Dans la suite de cette preuve on va supposer que n n 2 2 
A chaque appel récursif, on subdivise un problème de taille n n 2 2 n / 2 n/2 2 2 j j 2 j 2j n / 2 j n/2j n / 2 j n/2j 
Le coût d'un niveau j j 2 j × ( n 2 j ) = n 2j×(n2j)=n j j 
Pour mieux visualiser tout cela, voici l'arbre des appels récursifs, sur chaque sommet étant indiqué la taille du paramètre lors de l'appel en question :
On a supposé que n n 2 2 n n n = 2 h n=2h h h h = l o g 2 ( n ) h=log2(n) 
Le nombre de niveaux est alors égal à h + 1 h+1 i.e. l o g 2 ( n ) + 1 log2(n)+1 n n 
Le coût total est alors égal à la somme des coûts de tous les niveaux, i.e. n × ( l o g 2 ( n ) + 1 ) n×(log2(n)+1) 
 
 
Dans la preuve précédente, on a supposé que n n 2 2 2 2 
Nous ne rentrerons pas dans ces détails car ils n'apportent rien en terme de compréhension profonde.
 
 
 
 
 
  
 
 
 
Dans cette sous-partie nous n'allons plus nous limiter au cas du tri fusion, mais nous allons présenter et démontrer un résultat applicable à la majorité des algorithmes de type diviser pour régner 
Nous allons donc nous intéresser à des algorithmes de la forme :
def  divideAndConquer (n) :if  n ==1 :
        end of processing
    else :
        divideAndConquer(n//b)
        divideAndConquer(n//b)
        ... a times work of cost f(n) for  dividing the problem
            and  merging the solutions to
            the subproblems Un problème de taille n n a a n / b n/b f ( n ) f(n) 
 
Il est donc implicitement supposé que chaque sous-problème a la même taille. Dans le cas contraire le Master Theorem ne peut s'appliquer.
 
 
 
 
Master Theorem 
Soient a ≥ 1 a≥1 b > 1 b>1 T T 
T ( n ) = a T ( n b ) + f ( n ) T(n)=aT(nb)+f(n) 
Les différents paramètres s'interprètent comme suit :
n n 
 
a a 
 
n / b n/b 
 
f ( n ) f(n) 
 
 
 
On suppose de plus que f ( n ) = Θ ( n d ) f(n)=Θ(nd) 
Alors
T ( n ) = ⎧ ⎪ ⎪ ⎨ ⎪ ⎪ ⎩ Θ ( n d ) si  l o g b ( a ) < d Θ ( n d l o g ( n ) ) si  l o g b ( a ) = d Θ ( n l o g b ( a ) ) si  l o g b ( a ) > d T(n)={Θ(nd)si logb(a)<dΘ(ndlog(n))si logb(a)=dΘ(nlogb(a))si logb(a)>d 
On a également la formulation équivalente suivante
T ( n ) = ⎧ ⎪ ⎪ ⎨ ⎪ ⎪ ⎩ Θ ( n d ) si  a < b d Θ ( n d l o g ( n ) ) si a = b d Θ ( n l o g b ( a ) ) si  a > b d T(n)={Θ(nd)si a<bdΘ(ndlog(n))si a=bdΘ(nlogb(a))si a>bd 
 
 
Il n’est pas nécessaire de préciser la base du logarithme dans le deuxième cas, puisque rappelons le toutes les fonctions logarithmes diffèrent juste d’une constante multiplicative.
 
 
 
 
Comme lors de la démonstration de la complexité du tri fusion, nous allons étudier l'arbre des appels récursifs 
Nous aurons également besoin du petit lemme suivant :
Lemme 
Soit q ∈ R q∈ℝ 
Si q < 1 q<1 
r ∑ j = 0 q j ≤ 1 1 − q ∑j=0rqj≤11-q 
Si q > 1 q>1 
r ∑ j = 0 q j ≤ q r ( 1 + 1 q − 1 ) ∑j=0rqj≤qr(1+1q-1) 
 
Démonstration du lemme 
On sait (voir paragraphe 1.4 du chapitre, 1) que si q ≠ 1 q≠1 
r ∑ j = 0 q j = 1 − q r + 1 1 − q = q r + 1 − 1 q − 1 ∑j=0rqj=1-qr+11-q=qr+1-1q-1 
Si q < 1 q<1 1 − q r + 1 < 1 1-qr+1<1 
Si q > 1 q>1 q r + 1 − 1 < q r + 1 qr+1-1<qr+1 
r ∑ j = 0 q j ≤ q r + 1 q − 1 ∑j=0rqj≤qr+1q-1 
Il ne reste plus qu'à factoriser par q r qr 
 
Démonstration du Master Theorem 
Dans la suite de cette preuve on va supposer que n n b b 
A chaque appel récursif, on subdivise un problème de taille n n a a n / b n/b j j a j aj n / b j n/bj ( n / b j ) d (n/bj)d Θ ( n d ) Θ(nd) 
Le coût d'un niveau j j a j × ( n b j ) d aj×(nbj)d j j 
Pour mieux visualiser tout cela, voici l'arbre des appels récursifs, sur chaque sommet étant indiqué la taille du paramètre lors de l'appel en question :
On a supposé que n n b b n n n = b h n=bh h h h = l o g b ( n ) h=logb(n) 
Contrairement au tri fusion, chaque niveau n'a pas le même coût, et l'on ne peut donc avoir le coût total en multipliant le nombre de niveaux par le coût de chacun d'eux.
Pour estimer T ( n ) T(n) 
l o g b ( n ) ∑ j = 0 a j × ( n b j ) d ∑j=0logb(n)aj×(nbj)d 
On peut factoriser par n d nd j j 
l o g b ( n ) ∑ j = 0 a j × ( n b j ) d = n d l o g b ( n ) ∑ j = 0 ( a b d ) j ∑j=0logb(n)aj×(nbj)d=nd∑j=0logb(n)(abd)j 
On reconnaît la somme des premiers termes d'une suite géométrique de raison q = a b d q=abd a b d abd 1 1 
Cas où a = b d a=bd 
On a alors a b d = 1 abd=1 
n d l o g b ( n ) ∑ j = 0 ( a b d ) j = n d l o g b ( n ) ∑ j = 0 1 = n d × ( l o g b ( n ) + 1 ) nd∑j=0logb(n)(abd)j=nd∑j=0logb(n)1=nd×(logb(n)+1) 
D'où
T ( n ) = Θ ( n d l o g ( n ) ) T(n)=Θ(ndlog(n)) 
Cas où a < b d a<bd 
On utilise la première inégalité du lemme pour obtenir
n d l o g b ( n ) ∑ j = 0 ( a b d ) j ≤ n d 1 1 − a b d nd∑j=0logb(n)(abd)j≤nd11−abd 
D'où
T ( n ) = Θ ( n d ) T(n)=Θ(nd) 
Cas où a > b d a>bd 
On utilise cette fois la seconde inégalité du lemme. Il vient
n d l o g b ( n ) ∑ j = 0 ( a b d ) j ≤ n d ( a b d ) l o g b ( n ) ( 1 + 1 a b d − 1 ) nd∑j=0logb(n)(abd)j≤nd(abd)logb(n)(1+1abd−1) 
Quelques changements d'expressions dues aux règles opératoires usuelles sur les puissances conduisent à
n d b d l o g b ( n ) = n d b − d l o g b ( n ) = n d ( b l o g b ( n ) ) − d ndbdlogb(n)=ndb−dlogb(n)=nd(blogb(n))−d 
Or l'on sait que les fonctions logarithmes et exponentielles de base b b b l o g b ( n ) = n blogb(n)=n 
On a alors
n d ( b l o g b ( n ) ) − d = n d n − d = 1 nd(blogb(n))−d=ndn−d=1 
Cela prouve que
n d l o g b ( n ) ∑ j = 0 ( a b d ) j ≤ a l o g b ( n ) ( 1 + 1 a b d − 1 ) nd∑j=0logb(n)(abd)j≤alogb(n)(1+1abd−1) 
Et donc
T ( n ) = Θ ( a l o g b ( n ) ) T(n)=Θ(alogb(n)) 
Il ne reste plus qu'à constater que a l o g b ( n ) = n l o g b ( a ) alogb(n)=nlogb(a) b b l o g b ( a ) × l o g b ( n ) logb(a)×logb(n) 
 
 
Si n n b b encadrement 
 
 
 
 
Example 1.7. Retour sur le tri fusion 
Le tri fusion rentre bien sûr dans le cadre du Master Theorem.
A chaque appel récursif, on y subdivise un problème de taille n n 2 2 n / 2 n/2 a = 2 a=2 b = 2 b=2 
De plus la procédure de fusion est de complexité linéaire donc d = 1 d=1 
On est ainsi dans le cas où a = b d a=bd T ( n ) = Θ ( n d l o g ( n ) ) T(n)=Θ(ndlog(n)) i.e. T ( n ) = Θ ( n l o g ( n ) ) T(n)=Θ(nlog(n)) 
On retrouve bien le résultat démontré sur ce cas particulier au début de cette partie.
 
Pour terminer cette sous-partie, on propose au lecteur un petit exercice corrigé afin qu'il s'interroge sur les conditions d'application 
Exercice corrigé 
 
Expliquer dans chacun des cas suivants pourquoi le Master Theorem ne s'applique pas :
T ( n ) = 2 n T ( n 2 ) + n 3 T(n)=2nT(n2)+n3 
 
T ( n ) = T ( n 2 ) + T ( n 4 ) + n 2 T(n)=T(n2)+T(n4)+n2 
 
T ( n ) = 1 2 T ( n 2 ) + n T(n)=12T(n2)+n 
 
 
 
 
 
Display answer  
 
 
 
Si l'on se retrouve dans une situation où le Master Theorem ne s'applique pas, il faut considérer l'arbre des appels récursifs et l'analyser en détail. Un peu comme dans la preuve du théorème, mais en prenant bien sûr en compte les facteurs qui ont fait qu'il ne s'appliquait pas.
 
 
 
 
 
  
 
 
 
Livrons nous maintenant à une petite interprétation essence 
Rappelons que le coût d'un niveau j j a j × ( n b j ) d aj×(nbj)d n d × ( a b d ) j nd×(abd)j 
La constante a a vitesse de prolifération des sous-problèmes b d bd vitesse de diminition du coût de traitement par sous-problème 
Le fait que l'une de ces vitesses prenne le pas sur l'autre ou au contraire qu'elles se compensent expliquera la complexité finale.
Voici les trois situations 
Cas où a < b d a<bd j j n d nd 
 
Cas où a = b d a=bd j j n d nd n d nd l o g b ( n ) logb(n) 
 
Cas où a > b d a>bd j j i.e. n l o g b ( a ) nlogb(a) 
 
 
 
 
  
 
 
 
Les notations et leur signification seront les mêmes que dans la version dite simple, mais les hypothèses seront moins restrictives 
Nous présentons cette version du théorème par soucis de complétude, mais en général la précédente suffit. D'ailleurs nous n'en ferons pas de démonstration, les "nouveaux" arguments étant essentiellement mathématiques.
Master Theorem 
Soient a ≥ 1 a≥1 b > 1 b>1 T T 
T ( n ) = a T ( n b ) + f ( n ) T(n)=aT(nb)+f(n) 
Les différents paramètres s'interprètent comme suit :
n n 
 
a a 
 
n / b n/b 
 
f ( n ) f(n) 
 
 
 
Alors
T ( n ) = ⎧ ⎪ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎪ ⎩ Θ ( f ( n ) ) si  f ( n ) = Ω ( n d )  avec  l o g b ( a ) < d  et si  a × f ( n b ) ≤ c × f ( n )  avec  c < 1 Θ ( n l o g b ( a ) l o g ( n ) ) si  f ( n ) = Θ ( n d )  avec  l o g b ( a ) = d Θ ( n l o g b ( a ) ) si  f ( n ) = O ( n d )  avec  l o g b ( a ) > d T(n)={Θ(f(n))si f(n)=Ω(nd) avec logb(a)<d et si a×f(nb)≤c×f(n) avec c<1Θ(nlogb(a)log(n))si f(n)=Θ(nd) avec logb(a)=dΘ(nlogb(a))si f(n)=O(nd) avec logb(a)>d 
 
 
On trouve ce théorème sous cette forme générale dans l'excellent livre de Thomas H. Cormen Charles E. Leiserson Ronald L. Rivest Clifford Stein Introduction to algorithms 
Ne pas se fier au titre, ce livre est bien plus qu'une introduction, c'est une référence