Vous êtes sur la page 1sur 55

Récursivité. Paradigme « diviser pour régner ».

1ADS Algorithm in Python

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


Récursivité.  Paradigme  «  diviser  pour  régner  ».  

Objec&fs  de  ce  module  

• Présenter   la   no/on   simple   mais   néanmoins  


profonde  de  récursivité.  

• Introduire   une   méthode   de   programma/on  


appelée   «   diviser   pour   régner   »,   où   pour  
résoudre   un   problème   on   essaie   de   le  
«   découper   »   en   problèmes   similaires   plus  
simples,   puis   où   l’on   combine   les   résultats  
obtenus.  
©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  
Récursivité.  Paradigme  «  diviser  pour  régner  ».  

Plan  de  ce  module  

1. Récursivité.  
2. Paradigme  «  diviser  pour  régner  ».  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


Récursivité.  Paradigme  «  diviser  pour  régner  ».  

1.  RÉCURSIVITÉ.  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

Plan  de  ce  chapitre  

a. Principe.  
b. Récursivité  versus  itéra/on.  
c. Récursivité  croisée.  
d. Récursivité  mul/ple.  
e. Récursivité  imbriquée.  
f. Exercice.  
 
©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  
1.  Récursivité.  

a.  Principe.  
Défini&on  :  un  sous–programme  (procédure  ou  fonc/on)  est  
dit  récursif  s’il  s’appelle  lui  même.  
 
 
Idée  :  pour  effectuer  une  tâche  ou  un  calcul,  on  se  ramène  à  
la   réalisa/on   d’une   tâche   similaire   mais   de   complexité  
moindre.   On   recommence   jusqu’à   obtenir   une   tâche  
élémentaire.  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

a.  Principe.  
Exemple  classique  :  calcul  de  n!  =  1  x  2  x  3  x  …  x  n.    
 
On  montre  facilement  la  rela/on  de  récurrence  n!  =  n  x  (n-­‐1)!  
 
Si  on  sait  calculer  (n-­‐1)!,  on  connaîtra  donc  la  valeur  de  n!  
 
Mais  (n-­‐1)!  =  (n-­‐1)  x  (n-­‐2)!  
 
On  est  donc  ramené  au  calcul  de  (n-­‐2)!  
 
Et  ainsi  de  suite  jusqu’à  1!  dont  on  connaît  la  valeur  :  1  
©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  
1.  Récursivité.  

a.  Principe.  
Exemple  :  calcul  récursif  de  n!  

def factorielleRecursive(n):
if n == 0 or n == 1:
return 1
else:
return n*factorielleRecursive(n-1)

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

a.  Principe.  
Déroulement  du  programme  :  si  par  exemple  n  =  4  

facto(4)  =  4  x  facto(3)   facto(4)  =  4  x  3  =  12  

facto(3)  =  3  x  facto(2)   facto(3)  =  3  x  2  =  6  

facto(2)  =  2  x  facto(1)   facto(2)  =  2  x  1  =  2  

facto(1)  =  1  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

a.  Principe.  
Remarque  importante  :  
 
• Il   est   indispensable   de   prévoir   une   condi/on   d’arrêt   à   la  
récursion  sinon  le  programme  ne  se  termine  jamais.  

 
 

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

a.  Principe.  
Exemple  à  ne  pas  suivre  :  

def factorielleRecursiveBadJob(n):
return n*factorielleRecursiveBadJob(n-1)

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

b.  Récursivité  versus  itéra&on.  


• On  peut  toujours  transformer  un  algorithme  récursif  en  un  
algorithme  itéra/f  et  inversement.  

• L’algorithme  itéra/f  sera  plus  rapide  une  fois  implémenté  


dans   un   langage   de   programma/on   mais   souvent   plus  
complexe  à  écrire.  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

b.  Récursivité  versus  itéra&on.  


Exemple  :  version  itéra/ve  du  calcul  de  n!  

def factorielleIterative(n):
resultat = 1
for i in range(2,n+1):
resultat *= i
return resultat

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

b.  Récursivité  versus  itéra&on.  


Intérêts  de  la  récursivité  :  
 
• Technique  de  programma/on  très  élégante  et  lisible  (elle  
évite   souvent   le   recours   à   de   nombreuses   structures  
itéra/ves).  

• Elle   est   très   u/le   pour   concevoir   des   algorithmes   sur   des  
structures   complexes   comme   les   listes,   les   arbres   et   les  
graphes.  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

b.  Récursivité  versus  itéra&on.  


Inconvénient  majeur  de  la  récursivité    :  
 
• Une  fois  implémentée  dans  un  langage  de  programma/on,  
ce8e  technique  est  très  «  gourmande  »  en  mémoire.  

• Elle  peut  même  provoquer  des  débordements  de  capacité.  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

c.  Récursivité  croisée.  
Récursivité   croisée   :   c’est   un   cas   bien   par/culier   de  
récursivité  où  une  fonc/on  appelle  une  autre  fonc/on  qui  
elle  même  appelle  la  première.  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

c.  Récursivité  croisée.  
Exemple  :  test  de  la  parité  d’un  nombre  

def pair(n):
if n == 0:
return True
else:
return impair(n-1)

def impair(n):
if n == 0:
return False
else:
return pair(n-1)

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

d.  Récursivité  mul&ple.  
Récursivité   mul&ple   :   sous-­‐programme   récursif   réalisant  
plusieurs  appels  à  lui  même.    
 
Exemple  :  calcul  des  coefficients  binomiaux.  

 1 si k = 0 ou si k = n
 n  
  =   n −1   n −1 
 k    +  sinon
  k −1   k 

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

d.  Récursivité  mul&ple.  
Exemple  :  calcul  des  coefficients  binomiaux  

def coeffs(n,k):
if k == 0 or k == n:
return 1
else:
return coeffs(n-1,k-1) + coeffs(n-1,k)

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

e.  Récursivité  imbriquée.  
Récursivité   imbriquée   :   sous   programme   récursif   dont  
l’appel  à  lui  même  con/ent  un  autre  appel  à  lui  même.  
 
Exemple  :  fonc/on  91  de  McCarthy  définie  sur  Z  par    

 n −10 si n > 100



f (n) = 
 f ( f ( n +11)) si n ≤ 100

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

e.  Récursivité  imbriquée.  
Exemple  :  fonc/on  91  de  McCarthy    

def f91(n):
if n > 100:
return n-10
else:
return f91(f91(n+11))

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

f.  Exercice.  
Exercice   :   écrire   de   deux   façons   (itéra/ve   et   récursive)   une  
fonc/on  ayant  pour  paramètres  un  réel  x  et  un  en/er  n,  et  
retournant  la  valeur  de  x  puissance  n.  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

f.  Exercice.  
Solu&on  :  version  itéra/ve  

def puissanceIterative(x,n):
resultat = 1
for i in range(1,n+1):
resultat *= x
return resultat

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

f.  Exercice.  
Solu&on  :  version  récursive  

def puissanceRecursive(x,n):
if n == 0:
return 1
else:
return x*puissanceRecursive(x,n-1)

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


1.  Récursivité.  

Fin  de  ce  chapitre  

Avez  –  vous  des  ques/ons  ?  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


Récursivité.  Paradigme  «  diviser  pour  régner  ».  

2.  PARADIGME  «  DIVISER  POUR  


RÉGNER  ».  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

Plan  de  ce  chapitre  

a. Principe.  
b. Exemple  1  :  calcul  du  maximum  d’une  liste.  
c. Exemple  2  :  recherche  d’un  élément  dans  une  
liste.  
d. Exemple  3  :  algorithme  de  Karatsuba.  
 

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

a.  Principe.  
• Les  algorithmes  de  la  par/e  précédente  étaient  basés  pour  
la   plupart   sur   la   simple   exploita/on   d’une   formule   de  
récurrence.    

• Mais  beaucoup  de  problèmes  plus  complexes  se  résolvent  


aussi   naturellement   de   façon   récursive,   avec   des  
algorithmes   s’appellant   eux-­‐mêmes   une   ou   plusieurs   fois  
sur  des  données  de  tailles  inférieures.  

• Il   faudra   alors   combiner   les   résultats   issus   de   chacun   de  


ces  appels.  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

a.  Principe.  
Trois  étapes  :  
 
① Diviser   :   on   divise   les   données   ini/ales   en   plusieurs   sous-­‐
par/es.  

② Régner   :   on   résout   récursivement   chacun   des   sous-­‐


problèmes  associés  (ou  on  les  résout  directement  si  leur  
taille  est  assez  pe/te).  

③ Combiner   :   on   combine   les   différents   résultats   obtenus  


pour  obtenir  une  solu/on  au  problème  ini/al.  
©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  
2.  Diviser  pour  régner.  

a.  Principe.  
• On  va  se  familiariser  avec  ce  principe  général  sur  plusieurs  
exemples.  

• Pour   chacun   d’eux   on   présentera   en   détail   les   trois  


phases  :  diviser,  régner,  combiner.  

• La  recherche  de  ces  phases  se  fera  sur  des  exemples  lors  
des  séances  Labs.  

 
©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  
2.  Diviser  pour  régner.  

b.  Ex.  1  :  calcul  du  maximum  d’une  liste.  


• Problème  :  calculer  le  maximum  d’une  liste  de  nombres  

• Résolu&on  :  

① Diviser   la   liste   en   deux   sous-­‐listes   en   la   “coupant”   par   la  


moi/é.  
② Rechercher  le  maximum  de  chacune  de  ces  sous-­‐listes.  
③ Comparer  les  résultats  obtenus.  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

b.  Ex.  1  :  calcul  du  maximum  d’une  liste.  


Exercice   :   écrire   une   fonc/on   en   Python   implémentant  
l’algorithme  précédent.  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

b.  Ex.  1  :  calcul  du  maximum  d’une  liste.  


Solu&on  :  

def maximum(l,d,f):
if d == f:
return l[d]
m = (d+f) // 2
x = maximum(l,d,m)
y = maximum(l,m+1,f)
return x if x > y else y

maListe = [38,-3, 2, 1, 7]
print(maximum(maListe,0,4))

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

c.  Ex.  2  :  recherche  d’un  élément.  


• Problème  :   rechercher   la   présence   d’un   élement   dans   une  
liste.  

• Résolu&on  :  

① Diviser   la   liste   en   deux   sous-­‐listes   en   la   “coupant”   par   la  


moi/é.  
② Rechercher  la  présence  de  l’élément  dans  chacune  de  ces  
sous-­‐listes.  
③ Combiner  les  résultats  obtenus.  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

c.  Ex.  2  :  recherche  d’un  élément.  


Exercice   :   écrire   une   fonc/on   en   Python   implémentant  
l’algorithme  précédent.  
 

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

c.  Ex.  2  :  recherche  d’un  élément.  


Solu&on  :  

def recherche(l,x,d,f):
if d == f:
return l[d] == x
m = (d+f) // 2
return recherche(l,x,d,m) or
recherche(l,x,m+1,f)

maListe = [38,-3, 2, 1, 7]
print(recherche(maListe,7,0,4))

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

c.  Ex.  2  :  recherche  d’un  élément.  


• Problème  :   rechercher   la   présence   d’un   élement   dans   une  
liste  triée.  

• Idée  :  adopter  une  méthode  dichotomique.  

• La  liste  étant  triée,  il  est  facile  de  voir  dans  quelle  moi&é  
peut  éventuellement  se  trouver  l’élément  cherché.  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

c.  Ex.  2  :  recherche  d’un  élément.  


• Idée   (suite)   :   quand   on   cherche   un   élément   dans   une   liste  
triée,  après  comparaison  avec  l’élément  du  mileu  il  est  facile  
de  voir  dans  quelle  moi/é  con/nuer  la  recherche.  

-­‐5   -­‐1   0   3   4   10   666  

• Exemple   :   si   on   recherche   la   valeur   -­‐4,   étant   plus   pe/te   que  


la   valeur   centrale   3,   on   va   con/nuer   la   recherche  
uniquement  dans  la  première  par/e  de  la  liste.  
©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  
2.  Diviser  pour  régner.  

c.  Ex.  2  :  recherche  d’un  élément.  


• Problème  :   rechercher   la   présence   d’un   élement   dans   une  
liste  triée.  

• Résolu&on  :  

① Comparer   l’élément   recherché   avec   l’élément   situé   au  


milieu  de  la  liste.  
② Diviser   la   liste   en   deux   sous-­‐listes   en   la   “coupant”   par   la  
moi/é.  
③ Rechercher   la   présence   de   l’élément   dans   la   “bonne”   des  
deux  sous-­‐listes.  
④ Pas   de   résultats   à   combiner   puisque   l’on   ne   “travaille”   que  
sur  une  des  deux  sous-­‐listes.  
©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  
2.  Diviser  pour  régner.  

c.  Ex.  2  :  recherche  d’un  élément.  


Exercice   :   écrire   une   fonc/on   en   Python   implémentant  
l’algorithme   précédent.   On   supposera   donc   que   la   liste  
passée  en  paramètre  est  triée.  
 

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

c.  Ex.  2  :  recherche  d’un  élément.  


Solu&on  :    

def dicho(l,x,d,f):
if d > f:
return False
else:
m = (d+f) // 2
if l[m] == x:
return True
else:
if x < l[m]:
return dicho(l,x,d,m-1)
else:
return dicho(l,x,m+1,f)
©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  
2.  Diviser  pour  régner.  

c.  Ex.  2  :  recherche  d’un  élément.  


Solu&on  2  :  on  profite  ici  de  certaines  spécificités  du  Python  

def dicho2(l,x):
if len(l) == 0:
return False
else:
m = (len(l)-1) // 2
if l[m] == x:
return True
else:
if x < l[m]:
return dicho2(l[:m],x)
else:
return dicho2(l[m+1:],x)
©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  
2.  Diviser  pour  régner.  

c.  Ex.  2  :  recherche  d’un  élément.  


Remarque  sur  l’efficacité  des  algorithmes  de  recherche  :  
 
• Sans   rentrer   dans   les   détails,   on   voit   facilement   que   la  
recherche   par   dichotomie   est   moins   gourmande   en  
nombre  d’opéra/ons  que  l’autre.  

• Pour   la   première   méthode,   si   un   élément   n’appar/ent   pas  


à  une  liste  de  n  éléments  il  faudra  n  comparaisons  pour  le  
détecter.  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

c.  Ex.  2  :  recherche  d’un  élément.  


• Avec  une  recherche  dichotomique  il  faudra  seulement  de  
l’ordre  de  log2(n)  comparaisons.  

• Exemple  :  recherche  de  l’élément  -­‐4  dans  ce8e  liste,  il  faut  
trois  comparaisons  pour  conclure  à  son  absence.  

-­‐5   -­‐1   0   3   4   10   666  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

c.  Ex.  2  :  recherche  d’un  élément.  


Remarque  :  
 
• La   comparaison   de   l’efficacité   de   plusieurs   algorithmes  
résolvant  le  même  problème  est  étudiée  dans  la  “théorie  
de  la  complexité”.  

• Une   introduc/on   à   ce8e   ques/on   sera   abordée   en  


seconde  année.  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

d.  Ex.  3  :  algorithme  de  Karatsuba.  


Problème   :   mul/plier   deux   en/ers   par   une   méthode   plus  
rapide  que  celle  usuelle.  
 
Étude  d’un  exemple  :  imaginons  que  l’on  veuille  effectuer  le  
produit  12  x  34  
 
4  “pe/ts”  produits  sont  nécessaires    
si  l’on  u/lise  la  technique  habituelle  :  
 
 

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

d.  Ex.  3  :  algorithme  de  Karatsuba.  


Réécrivons  ce  calcul  :  

12 × 34 = (1×10 + 2) × (3×10 + 4)
= 3×10 2 + (1× 4 + 2 × 3) ×10 + 8
= 300 +100 + 8
= 408

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

d.  Ex.  3  :  algorithme  de  Karatsuba.  


Généralisons  le  à  tout  produit  de  nombres  à  2  chiffres  :  
 
  ab × cd = ( a ×10 + b) × (c ×10 + d )
  = a × c ×10 2 + ( a × d + b × c) ×10 + b × d
 
 
Or  :   ( a + b) × ( c + d ) = a × c + b × d + a × d + b × c
 
Donc  :       a × d + b × c = ( a + b) × (c + d ) − a × c − b × d

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

d.  Ex.  3  :  algorithme  de  Karatsuba.  


Conclusion  :  
 
2
 ab × cd = a × c ×10 + (( a + b) × ( c + d ) − a × c − b × d ) ×10 + b × d

 
• Et  là  on  s’aperçoit  qu’il  suffit  en  fait  de  réaliser  3  “pe/ts”  
produits  contre  4  dans  la  méthode  usuelle.  

• Ce  que  l’on  vient  de  faire  sur  des  nombres  à  2  chiffres  on  
peut  bien  sûr  le  faire  sur  des  nombres  à  n  chiffres,  en  les  
coupant  en  deux  par/es  de  n/2  chiffres.  
 
 
©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  
2.  Diviser  pour  régner.  

d.  Ex.  3  :  algorithme  de  Karatsuba.  


Généralisa&on  à  des  nombres  à  n  chiffres  :  
 
   n   n  n
n

 a ×10 2 + b  ×  c ×10 2 + d  = a × c ×10 + ( a × d + b × c) ×10 2 + b × d


     
 
• Et  là  encore  on  u/lisera  le  calcul    

a × d + b × c = ( a + b) × ( c + d ) − a × c − b × d

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

d.  Ex.  3  :  algorithme  de  Karatsuba.  


• Problème  :  calculer  le  produit  de  deux  nombres  

• Résolu&on  :  

① Séparer   chaque   nombre   en   deux   par/es   de   n/2   chiffres   où  


n  est  le  nombre  de  chiffres  du  plus  grand  des  deux.  
n n
x = a ×10 + b et y = c ×10 + d
2 2

② Effectuer  les  trois  produits.   a × c, b × d, ( a + b) × ( c + d )


③ Combiner  les  résultats  obtenus.  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

d.  Ex.  3  :  algorithme  de  Karatsuba.  


Exercice   :   écrire   une   fonc/on   en   Python   implémentant  
l’algorithme   précédent.   On   s’autorise   le   droit   d’u/liser   les  
opérateurs  **,  //  et  %.  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

d.  Ex.  3  :  algorithme  de  Karatsuba.  


Solu&on  :  

def karatsuba(x,y):
if x < 10 or y < 10:
return x*y
m = max([len(str(x)),len(str(y))])
n = (m+1)//2
z = 10**n
a, b = x//z, x%z
c, d = y//z, y%z
u = karatsuba(a,c)
v = karatsuba(a+b,c+d)
w = karatsuba(b,d)
return u*(10**(2*n))+(v-u-w)*(10**n)+ w

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


2.  Diviser  pour  régner.  

Fin  de  ce  chapitre  

Avez  –  vous  des  ques/ons  ?  

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  


Récursivité. Paradigme « diviser pour régner ».

Algorithmes de tri.

©  SUPINFO  Interna/onal  University  –  h8p://www.supinfo.com  

Vous aimerez peut-être aussi