Chapitre 1: Récursivité 1 Rappel : la otio de foctio Cette sectio est u bref rappel de la otio de foctio e iformatique, et so utilisatio e Pytho 11 Défiitio E iformatique, ue foctio est ue routie qui retoure ue valeur 1 Sas être opposés, deux poits de vue cohabitet das la otio de foctio : D ue part, utiliser des foctios permet de découper le programme gééral e algorithmes plus courts et plus simples D autre part, ue foctio est ue séquece d istructios ayat u rôle bie précis Das la défiitio d ue foctio, o trouve e gééral des paramètres d etrée, dot déped la valeur de sortie, et qu o appelle argumets Ue foctio e iformatique diffère d ue foctio mathématique car elle peut agir sur so eviroemet : deux appels idetiques à la foctio e produirot pas écessairemet le même résultat, certaies variables globales peuvet être modifiées, etc (das le cas cotraire, la foctio est dite pure) Preos quelque exemples, e Pytho : pour déclarer ue foctio, o utilise le mot clef def def somme(x,y): z=x+y retur(z) def bojour(): prit("hello World!") retur def icremeter(l,c): for i i rage(le(l)): L[i]=L[i]+c Das les exemples précédets, seule la première foctio est pure La deuxième agit sur l eviroemet e affichat u message à l écra 2, la troisième icrémete toutes les etrées du tableau L de la valeur c Remarquez que : La foctio bojour e pred e etrée aucu argumet Les foctios bojour et icremeter e retouret rie Plus exactemet, elles retouret toutes deux u objet appelé Noe : o aurait égalemet pu écrire retur(noe) Das ce cas, o peut parler de procédure, mais la déclaratio e Pytho se fait de la même maière que pour ue foctio Notez que le mot clef retur iterrompt immédiatemet la foctio pour reveir au poit où elle a été appelée : vous pouvez ajouter prit(1/0) derrière u retur, cela e produira pas d erreur 12 Variables locales et globales Reveos à la foctio somme défiie plus haut La variable z qui y est défiie est locale : so utilisatio est strictemet limitée à la foctio somme Par exemple, l exécutio du code z=2 ; s=somme(4,5) ; prit(z) affiche la valeur 2 à l écra A cotrario, ue variable o locale est dite globale Par défaut e Pytho, ue variable est locale sauf si l u (au mois) des cas suivats est vérifié : elle est explicitemet déclarée globale elle est utilisée sas être affectée Das les exemples ci-dessous, les variables sot globales La troisième produit ue erreur, car o affecte ue variable () qui a pas été déclarée globale 1 Dixit Wikipédia, mais cette courte défiitio est plutôt correcte 2 Plus rigoureusemet, sur la sortie stadard Svartz Page 1/12 2014/2015
def ic_(): global =+1 def afficher_s(): prit(s) retur def foctio_erreur(): x= =2*x E gééral, o évitera l utilisatio de variables globales, sauf pour représeter les costates d u problème, par exemple 2 Foctios Récursives 21 Défiitio La défiitio d ue foctio récursive est plutôt simple : c est ue foctio qui s appelle elle-même Preos u exemple classique : le calcul de la factorielle O défiit, pour 0,! = i=1 i Iformatiquemet, o peut doc défiir la factorielle comme : def fact(): assert >=0, " doit etre positif" f=1 for i i rage(1,+1): f=f*i retur(f) Ue autre défiitio mathématique classique de la factorielle se fait par récurrece : { 1 si = 0! = ( 1)! sio E repreat quasimet mot pour mot cette derière défiitio, o obtiet la foctio Pytho suivate : def fact_rec(): if ==0: retur(1) else: retur(*fact(-1)) 22 La pile d exécutio Lorsqu o fait des appels de foctios la pile d appels de foctios pour mémoriser l edroit du programme où la foctio a été appelée, avec quels paramètres, etc E particulier, lors d appels imbriqués c est à dire lorsqu à l itérieur d ue foctio f o appelle ue foctio g, ce qui est relatif à l appel de la foctio g est placé juste au dessus de ce qui est relatif à la foctio f Lorsqu ue foctio a termié so exécutio, elle est dépilée Lors de l exécutio d ue foctio récursive, o appelle la même foctio plusieurs fois, ce qui produit plusieurs empilages : =0, factorielle(0)=1 =1, factorielle(1)=1*factorielle(0) =2, factorielle(2)=2*factorielle(1) =3, factorielle(3)=3*factorielle(2) =4, factorielle(4)=4*factorielle(3) Figure 1 La pile d exécutio lors de l appel de fact(4) Ue fois arrivé à u cas termial, o peut commecer à dépiler Das le cas de la foctio factorielle, comme celle-ci e s appelle qu ue seule fois, dés lors qu o a commecé à dépiler o e s arrête plus (voir la figure 2) Efi, la récursio s arrête lorsqu o dépile l élémet correspodat au premier appel de la foctio Svartz Page 2/12 2014/2015
=3, factorielle(3)=3*2 =4, factorielle(4)=4*factorielle(3) Figure 2 La pile d exécutio lors de l appel de fact(4) : o a dépilé les appels relatifs à = 0, = 1 et = 2 =4, factorielle(4)=24 Figure 3 La pile d exécutio lors de l appel de fact(4) : juste avat dépilage de l appel iitial O voit que ce le ombre d appels imbriqués réalisés par ue foctio récursive est importat : il faut stocker ces appels, ce qui coûte de la mémoire E Pytho, il est impossible de dépasser u certai ombre d appels récursifs, o e reparle das la sous-sectio qui suit 23 Limites de la récursivité L usage de la récursivité présete deux icovéiets : il faut faire attetio à ce que les appels récursifs e se chevauchet pas, et predre garde à e pas faire u trop grad ombre d appels récursifs imbriqués Chevauchemet des appels récursifs Cosidéros l exemple suivat : soit (F ) N la suite défiie par { 1 si = 0 ou 1 F = F 2 + F 1 sio Vous aurez probablemet recou la fameuse suite de Fiboacci Ue trascriptio récursive e Pytho s obtiet aisémet : def Fib_rec(): assert >=0 if ==0 or ==1: retur(1) retur(fib_rec(-1)+fib_rec(-2)) Le problème de l algorithme précédet est le ombre d appels récursifs effectués Notos A le ombre d appels récursifs écessaires pour le calcul de F Alors, la suite (A ) vérifie la relatio de récurrece A 0 = A 1 = 0 et pour tout 2, A = 2 + A 1 + A 2, soit A + 2 = (A 2 + 2) + (A 1 + 2) Autremet dit, la suite (A + 2) N coïcide avec la suite (2F ) N O peut doer ue expressio explicite de A, à savoir : Pour tout etier aturel, A = 2 5 ( ) +1 ( 5 + 1 2 1 5 2 ) +1 ( 5+1 ) mais ce qui importe, c est que A C + 2 où C est ue costate strictemet positive U appel récursif est jamais gratuit (et possède u coût mioré par ue costate strictemet positive), aisi le calcul de F par cette méthode est expoetiel, puisque 5+1 2 > 1 Il vaut mieux utiliser ue méthode itérative das ce cas, comme la suivate 3 : def Fib(): a,b=1,1 for i i rage(-1): a,b=b,a+b retur(b) O peut observer que la versio itérative est mois claire que la versio récursive Elle a l avatage de s effectuer e temps liéaire e 4 E coclusio, il faut faire attetio à e pas faire des appels récursifs qui se recoupet, sous peie de voir la complexité exploser! 3 o peut cepedat s e sortir avec ue versio récursive ayat u coût liéaire 4 Ue autre remarque : cette foctio permet de calculer tous les termes de la suite O peut faire mieux si o cherche uiquemet le -ième, voir TD! 2 Svartz Page 3/12 2014/2015
Taille de la pile (Pytho) Supposos que l o souhaite calculer!, pour relativemet grad 5, disos 10000 A priori, les deux versios (itérative et récursive) fot l affaire Or l appel à fact_rec(10000) produit u message d erreur dot l ititulé est RutimeError: maximum recursio depth exceeded E d autres termes, le ombre maximal d appels de foctios imbriqués a été atteit E Pytho, ce iveau est fixé à 1000 Cette valeur arbitraire peut-être augmetée 6 mais elle est là pour éviter u dépassemet de capacité de la pile d appels : le fameux «stack overflow» 7 Temps d exécutio : itératif cotre récursif Stocker systématiquemet l état d ue foctio avat chaque appel récursif das la pile d appels est pas gratuit, e temps comme e mémoire Si la formulatio itérative s obtiet facilemet, il est e gééral préférable de l utiliser 3 Termiaiso et Correctio d ue foctio récursive Motrer la termiaiso d ue foctio sigifie démotrer que le processus récursif s arrête pour tous paramètres Démotrer sa correctio sigifie prouver que la foctio a bie le comportemet attedu 31 Termiaiso Pour motrer la termiaiso d ue foctio récursive, il suffit d exhiber ue foctio des paramètres à valeurs das N, telle que les valeurs prises par la foctio décroisset strictemet au cours des appels récursifs successifs Pour la foctio factorielle, il suffit de predre le paramètre lui-même Cosidéros u exemple où la foctio e questio est (u peu) mois évidete : la recherche dichotomique das u tableau trié, qui est explicitemet au programme (au mois das sa versio itérative) def cherche_dicho_rec(l,x): """L tableau o vide trié das l ordre croissat, x u élémet O revoie True si x est das L, False sio""" =le(l) #la logueur du tableau if ==1: retur(l[0]==x) m=//2 if L[m]==x: retur(true) elif L[m]<x: retur(cherche_dicho_rec(l[m:],x)) #la partie gauche du tableau else: retur(cherche_dicho_rec(l[:m],x)) #la partie droite La foctio cherche_dicho_rec(x,l) retoure u boolée caractérisat le fait que x est das L ou o L idée de la recherche dichotomique est simple : o coupe le tableau L e deux, au iveau de l idice m=//2 Comme le tableau est trié, la comparaiso de L[m] avec x suffit pour savoir si o a trouvé x e positio m ou alors das quelle partie (gauche ou droite) du tableau x a aucue chace de se trouver Das ce derier cas, il suffit de lacer u appel récursif sur la moitié de tableau où x est peut-être Commet prouver la termiaiso de la foctio cherche_dicho_rec? La quatité qui ous itéresse ici est le(l)-1 : e effet, si L est o vide (ce qu o suppose), alors les sous-tableaux L[m:] et L[:m] ot pour tailles m et m, où est la taille de L Puisque la foctio s arrête pour = 1, est au mois égal à 2 e cas d appel récursif Par coséquet, m = 2 > 0, aisi m et m sot tous deux strictemet compris etre 0 et Aisi le(l)-1 est das N et dimiue strictemet etre deux appels récursifs successifs Aisi, la foctio termie Il est pas toujours évidet d exhiber ue quatité qui décroît das ue foctio récursive Le faire pour la foctio de Syracuse suivate permettrait de résoudre u problème ouvert def Syracuse(): assert >=1 prit() if ==1: 5 Rappelos à toute fi utile que les etiers e sot pas borés e Pytho 6 À l aide de la foctio setrecursiolimit du module sys 7 Sur mo ordiateur persoel, le calcul de! avec la méthode récursive e passe plus à partir de 20000 eviro Svartz Page 4/12 2014/2015
retur(1) elif %2==0: retur(syracuse(//2)) else: retur(syracuse(3*+1)) Ue remarque pour termier : la quatité qui décroît est pas écessairemet à chercher das N, u esemble ordoé ayat pas de suite ifiie strictemet décroissate suffit C est par exemple le cas de N 2 ordoé par l ordre défii comme suit : 32 Correctio (x 0, y 0 ) (x 1, y 1 ) x 0 < x 1 ou x 0 = x 1 et y 0 < y 1 Pour prouver la correctio d ue foctio récursive, o procède e gééral par récurrece : o prouve d abord que la sortie de la foctio est correcte pour les cas termiaux, ce qui correspod à l iitialisatio de la récurrece, et o motre esuite que les appels récursifs se ramèet à des istaces plus petites (das le même ses que la termiaiso), ce qui costitue l hérédité La plupart du temps, la correctio est facile à prouver Par exemple pour la foctio cherche_dicho_rec défiie plus haut, o cosidère la propositio suivate : Si L est u tableau de logueur o ulle trié das l ordre croissat et x u élémet comparable à ceux de L, alors cherche_dicho_rec(l,x) retoure True si et seulemet si x est das L, False sio Iitialisatio : si la logueur du tableau est 1, alors la sortie de la foctio est correcte Hérédité : si L est de logueur au mois 2, u et u seul des cas suivats se produit : L[m] est égal à x, auquel cas la sortie de la foctio est correcte L[m] est iférieur strictemet à x, auquel cas x e peut se trouver qu après m puisque L est trié das l ordre croissat Le tableau L[m:] correspod aux élémets placés après m (iclus), triés égalemet das l ordre croissat Par hypothèse de récurrece, la sortie de la foctio cherche_dicho_rec sur l istace (L[m:],x) est correcte, doc égalemet sur (L,x) O procède de même,si L[m] est strictemet supérieur à x avec le tableau L[:m] Par pricipe de récurrece, la foctio cherche_dicho_rec est correcte 4 Complexité 41 Brefs rappels Qu est-ce que la complexité? L étude de la complexité d ue foctio cosiste à estimer so coût (temporel ou e mémoire), e foctio des etrées Pour différecier deux etrées etre elles, o compare e gééral leur taille Essetiellemet pour ous, les etrées serot costituées d etiers ou de tableaux Pour les tableaux, la doée pertiete est la taille Pour les etiers, cela déped du cotexte Pour u etier, o peut e effet exprimer la complexité d ue foctio dépedat de e foctio : de l etier lui-même ou de so ombre de chiffres (sa taille), correspodat à l(), à ue costate multiplicative près E gééral o e tiet pas compte des costates multiplicatives Par exemple, pour la foctio factorielle ci-dessus, le ombre d opératios est clairemet liéaire e Pour exprimer la complexité d ue foctio qui revoie l écriture e base 2 d u ombre exprimé e base 10, o se dirigerait plus aturellemet vers log 2 () Coûts Cocetros-ous d abord sur la complexité e temps L exécutio d u algorithme est ue séquece d opératios écessitat plus ou mois de temps Pour mesurer ce temps, o cosidère certaies opératios comme élémetaires : par exemple faire ue opératio arithmétique de base (additio, multiplicatio, soustractio, divisio), lire ou modifier u élémet d u tableau, ajouter u élémet à la fi d u tableau, affecter u etier ou u flottat, etc Estimer le coût d ue foctio sur ue etrée de taille doée sigifie estimer le ombre de ces opératios élémetaires effectuées par la foctio sur l etrée La complexité e mémoire cosiste à estimer la mémoire écessaire à ue foctio pour so exécutio Svartz Page 5/12 2014/2015
Différetes complexités Les otios de complexité au programme sot les complexités temporelles das le meilleur et das le pire cas, c est à dire le ombre miimal/maximal que requiert l algorithme pour s exécuter sur ue etrée de taille La complexité das le meilleur des cas est e gééral pas la plus pertiete Par exemple pour la foctio cherche_dicho_rec, si x se trouve e positio m, la foctio s arrête et doc la complexité das le meilleur de cas est costate La complexité au pire est le ombre d opératios maximales écessaires pour traiter ue etrée de taille doée Pour la foctio cherche_dicho_rec, le cas le pire se produit par exemple lorsque le tableau e cotiet pas x Ue autre otio de complexité itéressate est la complexité e moyee, qu o abordera u peu das le chapitre sur les tris E coclusio, lorsqu o demadera d exprimer la complexité d ue foctio sas plus de précisio, o sous-etedra la complexité das le pire cas Complexité asymptotique et otatios de Ladau Tout d abord, lorsque l o s itéresse à la complexité C() ( est la taille de l etrée) d ue foctio, c est bie souvet pour les grades valeurs de qu il est pertiet de coaître C(), pour comparer vis à vis d autres foctios réalisat le même calcul O cherche doc u comportemet asymptotique de, qu o rapportera aux foctios usuelles : logarithmes, puissaces, expoetielles Esuite, o e cherchera pas systématiquemet u équivalet : celui-ci est souvet difficile à obteir et est pas le plus importat Si deux foctios écessitet respectivemet 9 log() et 2 2 opératios élémetaires, o retiedra que la première écessite de l ordre de log() opératios, ce qui est bie meilleur que la secode qui e requiert de l ordre de 2 Rappelos les otatios de Ladau Soit f et g deux foctios N R O ote : f() = O(g()), si il existe u etier 0 tel que g() est o ul pour 0 et ( ) f() g() est borée 0 f() = Ω(g()), si g() = O(f()) f() = Θ(g()), si f() = O(g()) et f() = Ω(g()) Pour exprimer la complexité C(), o se cotetera bie souvet d u O Par exemple, la recherche dichotomique das u tableau trié de taille a ue complexité de O(l()) Si l o veut préciser que cette bore est essetiellemet optimale, o dira que la complexité est e Θ(l()) Attetio à e pas dire «la complexité est au mois e O(l())», ce qui aurait aucu ses Complexité arthmétique versus complexité biaire O voit facilemet que le calcul de! avec fact ou fact_rec écessite O() multiplicatios Aisi, la complexité (au pire comme au mieux, puisqu il y a qu ue seule etrée de «taille» ici) est e O(), puisque ce sot les seuls opératios effectuées Ce raisoemet est exact, e oubliat pas que l o parle ici de complexité arithmétique E pratique, les etiers iterveat das le calcul gradisset vite : teir compte de cette croissace doerait ue complexité dite biaire O s itéressera uiquemet à la complexité arithmétique, mais il faut avoir à l esprit que cela e reflète pas forcémet fidèlemet les temps de calculs effectifs 42 Complexité des foctios récursives La factorielle O a déja dit que! se calculait e complexité liéaire avec la foctio fact_rec Vérifios-le Si est ul, il y a rie à faire d autre que de retourer 1, ce qui se fait avec ue opératio élémetaire Sio, il faut multiplier le résultat de fact_rec(-1) par, ce qui pred doc 1 appel récursif, et 1 opératio arithmétique, soit 2 opératios élémetaires O a doc { 1 si = 0 C() = 2 + C( 1) sio O vérifie immédiatemet que C() = 2 + 1 est la solutio de cette récurrece, o e déduit doc que C() = Θ() O remarque que l o serait arrivé à la même coclusio si les costates iterveat das l équatio (1 et 2) étaiet différetes De plus, le poit termial peut-être situé e dessous de importe quel etier 0, sas chager le résultat : o utilisera doréavat les otatios de Ladau directemet das les récurreces Recherche dichotomique récursive aisi : Supposos que = 2 k est ue puissace de 2 Alors C() = C(/2) + O(1), C() = C(/2) + O(1) = C(/4) + O(1) + O(1) C() = ko(1) + C(1) = O(k) C() = O(l()) Svartz Page 6/12 2014/2015
Pour que le raisoemet suivat soit juste, il est impératif que les différets O(1) soiet idetiques, c est à dire qu il e dépedet pas de Certais auteurs utiliset des idices : O 0 (1),, O k (1) et e précisat que O o (1) = = O k (1) Remarquez égalemet que le raisoemet aurait pu être meé avec Θ à la place de O, car o cosidère le pire cas : celui où o est obligé de faire tous les appels récursifs jusqu à ue liste réduite à u élémet Das le cas où est pas ue puissace de 2, o s aperçoit que le résultat est le même : o bore la taille des tableaux obteus das les appels récursifs successifs par celles des tableaux que l o aurait eu si le tableau iitial était de taille la puissace de 2 immédiatemet supérieure (vous suivez?) Et comme log 2 () + 1 log 2 () + 1 = O(log 2 ()), le résultat suit 43 Le paradigme diviser pour réger : u exemple O doe u exemple faisat iterveir plusieurs appels récursifs à chaque étape Soit u etier strictemet positif Cosidéros P = 1 k=0 p kx k et Q = 1 k=0 Xk deux polyômes de même taille (la taille est égale au degré, plus 1) Les coefficiets sot das u aeau commutatif A quelcoque, qu o pourra cosidérer comme état l esemble des etiers Z : ue opératio das A (multiplicatio, additio, soustractio) est cosidérée comme élémetaire O représete les polyômes P et Q par les tableaux [p 0, p 1,, p ] et [q 0, q 1,, q ] Le produit P Q est u polyôme de logueur 2 1 représeté par u tableau [c 0, c 1,, c 2 2 ] Quelle est la complexité du calcul du tableau des (c i )? Méthode aïve Écrivos explicitemet le produit P Q : P Q = = = P Q = ( 1 ) p k X k k=0 p k q i X i+k 1 1 k=0 i=0 1 d=0 1 d=0 k=0 ( 1 ) q i X i i=0 (k,i) tel que i+k=d d p k q d k X d p k q i X d avec p i, q i uls pour i Autremet dit, o obtiet les formules classiques : c 0 = p 0 q 0 c 1 = p 1 q 0 + p 0 q 1 c 1 = p 0 q 1 + p 1 q 2 + + p 1 q 0 c 2 2 = p 1 q 1 Comptos le ombre d opératios élémetaires écessaires pour calculer l esemble des c i Tout d abord, il faut 1 + 2 + 3 + + ( 1) + + ( 1) + + 1 = ( 1) + = 2 multiplicatios De plus pour chaque c i, ue additio de mois que le ombre de multiplicatios est écessaire, soit 2 (2 1) = ( 1) 2 au total E résumé, le ombre d opératios élémetaires est Θ( 2 ), qui est quadratique e L algorithme de Karatsuba O remarque qu o peut facilemet abaisser le ombre de multiplicatios lorsque = 2 avec l idée suivate : o cosidère les produits t 0 = p 0 q 0 t 1 = p 1 q 1 t 2 = (p 0 + p 1 ) (q 0 + q 1 ) Alors le terme c 1 = p 0 q 1 +p 1 q 0 se déduit des produits ci-dessus car c 1 = t 2 t 1 t 0 Aisi, seulemet 3 multiplicatios sot écessaires lorsque = 2, au lieu de 4 avec la méthode aïve Certes, le ombre d additios/soustractios est Svartz Page 7/12 2014/2015
passé de 1 à 4, ce qui fait passer le ombre d opératios élémetaires de 5 à 7 Mais la récursivité va ous permettre d obteir u gai substatiel avec cette idée Cosidéros maiteat que est quelcoque Si = 1, o a pas trop le choix, o retoure le tableau [p 0 q 0 ] Sio, posos m = m 1 1 2, et découpos os polyômes e 2 O écrit doc P = p k X k +X m p k X 1 m et de même Q = Q 0 + X m Q 1 Alors, comme pour le cas = 2, o pose T 0 = P 0 Q 0 T 1 = P 1 Q 1 T 2 = (P 0 + P 1 ) (Q 0 + Q 1 ) k=0 } {{ } P 0 k=m } {{ } P 1 et comme P Q = (P 0 + X m P 1 )(Q 0 + X m Q 1 ) = P 0 Q 0 + X m (P 1 Q 0 + P 0 Q 0 ) + X 2m P 1 Q 1, o obtiet le produit e combiat les facteurs (T i ) de la faço suivate : P Q = T 0 + X m (T 2 T 1 T 0 ) + X 2m T 1 Évidemmet, les produits (T i ) sot eux-même calculés récursivemet e exploitat cette idée L algorithme, e pseudo-code 8, est le suivat : Algorithme 1 : L algorithme de Karatsuba Etrées : Deux polyômes P et Q de même taille Sortie : Le produit P Q si = 1 alors retourer (P Q) sio m = 2 ; Décomposer P = P 0 + X m P 1, Q = Q 0 + X m Q 1 ; T 0 =Karatsuba(P 0, Q 0 ); T 1 =Karatsuba(P 1, Q 1 ); T 2 =Karatsuba(P 0 + P 1, Q 0 + Q 1 ); retourer T 0 + X m (T 2 T 1 T 0 ) + X 2m T 1 Étude de la complexité de l algorithme de Karatsuba Supposos pour simplifier que est ue puissace de 2, doc s écrit 2 k Das chaque appel récursif, les istaces ot des tailles divisées exactemet par 2 Pour détermier la complexité globale, il est essetiel d estimer la complexité des deux étapes diviser et réger Ici, o suppose qu o e compte que les opératios arithmétiques das l aeau A (additios, multiplicatios, soustractios) Pour diviser, il faut créer les tableaux associés aux polyômes P 0 et P 1, et calculer les polyômes P 0 + P 1 et Q 0 + Q 1 Ceci se fait e temps liéaire e Pour réger, il faut calculer T 2 = T 2 T 0 T 1, créer u tableau de taille (2 1), et combier les trois tableaux associés à T 0, T 1 et T 2 pour obteir le tableau fial Ceci se fait égalemet e temps liéaire 9 e puisqu à u élémet du tableau fial correspod au plus u élémet de chaque tableau associé à T 0, T 1 et T 2 Pour le calcul du produit, o fait trois appels récursifs pour résoudre des problèmes de taille divisée par 2 Aisi, la complexité C() de l algorithme de Karatsuba satisfait à l équatio : dot la solutio est C() = 3 C(/2) + O() C() = O( l(3) l(2) ) Rédigeos la preuve très propremet Il existe deux costates strictemet positives A et B (o va voir qu elles e jouet aucu rôle) telles que { 1 si = 1 C() 3 C(/2) + A + B sio 8 U code Pytho est dispoible séparemmet et est égalemet proposé à la fi du chapitre Mais il est u peu log et l essetiel est de compredre l idée, pas exactemet commet o maipule les tableaux de coefficiets! 9 Jetez u œil au code Pytho si cela e vous paraît pas clair, c est immédiat Svartz Page 8/12 2014/2015
Rappelos que l o a supposé = 2 k Aisi, C() 3C(/2) + A + B C() 3(3(C(/4) + A/2 + B)) + A + B = 9C(/4) + A(1 + 3 2 ) + B(1 + 3) 9(3C(/8) + A/4 + B) = 27C(/8) + A(1 + 3 2 + 9 4 ) + B(1 + 3 + 9) C() [ ] 3 k C(1) + A 1 + 3 2 + + 3k 1 + B(1 + 3 + + 3 k 1 ) 2 k 1 3 k + 3k 1 3 1 B + A 3k /2 k 1 3/2 1 C() = O(3 k ) car = 2 k Or 3 k = 2 log 2 (3k) = 2 k log 2 (3) = (2 k ) log 2 (3) = log 2 (3) = l(3) l(2) Puisque l(3)/ l(2) = 158 l utilisatio de l algorithme de Karatsuba produit u gai substatiel comparé à l algorithme aïf Il reste à résoudre le cas où est pas ue puissace de 2 Das ce cas, la récurrece s écrit ( ) ( ) C() C + 2C + O() 2 2 où est la partie etière supérieure E déroulat la récurrece, o s aperçoit que les coûts de chaque iveau sot majorés par les coûts que l o aurait eu e cosidérat à la place de la puissace de 2 immédiatemet supérieure, à savoir 2 log 2 () La bore asymptotique C() = O( l(3) l(2) ) reste doc valable Ue remarque pour fiir : o peut e fait faire beaucoup mieux : il est possible de calculer le produit de deux polyômes de taille avec O( l()) opératios élémetaires Et e pratique? Le tableau suivat motre les temps e secodes écessaires e Pytho pour calculer sur mo ordiateur persoel (Pocket PC de 2012) le produit de deux polyômes P et Q avec de petits coefficiets etiers (tirés aléatoiremet das l itervalle [ 1000, 1000]), de degrés variables, avec u algorithme aïf et avec l algorithme de Karatsuba deg(p ) = deg(q) = 100 500 1000 5000 10000 50000 multiplicatio aïve 0006 016 063 1596 633 1692 Karatsuba 007 015 048 775 232 288 Pour mieux apercevoir les variatios, o peut tracer le diagramme log-log de ces temps U complexité C() = α doe ue droite car log(c()) = α log(), ce qu o observe sur le graphe suivat Ue régressio liéaire fait apparaître des coefficiets directeurs proches des valeurs théoriques 2 et 158 10 3 temps e secodes 10 2 10 1 10 0 10 1 10 3 10 4 (degré des polyômes) Figure 4 Comparaiso des temps (algorithme de Karatsuba, multiplicatio aïve), échelle log-log Svartz Page 9/12 2014/2015
44 Le paradigme diviser pour réger : explicatios O peut maiteat expliquer la termiologie «diviser pour réger» : O commece par diviser le problème e istaces de tailles plus petites : das l algorithme de Karatsuba, le problème de la multiplicatio de deux polyômes se ramèe à 3 istaces de la multiplicatios de taille divisée par 2 O calcule récursivemet les solutios aux sous-problèmes O «rège» : o recompose la solutio à partir des solutios des sous-problèmes Das beaucoup de problèmes qui admettet ue solutio du type diviser pour réger, ue telle approche permet d abaisser la complexité du processus de résolutio, et c est là que l usage de la récursivité se révèle utile : il est souvet bie difficile d exhiber u algorithme itératif résolvat le problème Du poit de vue de la complexité, beaucoup (pas toutes!) de stratégies du type diviser pour réger vérifiet ue équatio de la forme : C() = a C(/b) + O( α ) où a est le ombre de sous problèmes à cosidérer et 1/b est le «facteur de réductio» : o découpe le problème iitial e sous problèmes de taille divisée par b Le terme O( α ) représete la complexité du calcul des istaces de chaque sous-problème, et la recompositio des solutios des sous-problèmes de taille /b pour obteir la solutio de taille Das ce terme, α est souvet u etier strictemet positif O peut motrer le théorème suivat (hors programme) : Théorème 1 (Complexité des stratégies «diviser pour réger») Supposos qu il existe deux costates a et b, avec a 1 et b > 1, et u réel α 0 tels que C() vérifie : C() = a C(/b) + O( α ) et C() C 0 pour 0, avec C 0 ue costate Alors, trois cas peuvet se préseter : Si a > b α, alors C() = O( l(a) l(b) ) Si a < b α, alors C() = O( α ) Si a = b α, alors C() = O( α l()) Das le théorème précédet, la situatio est la suivate : les appels récursifs produiset u arbre d appels successifs Tout e haut de l arbre (voir figure 5) se trouve le problème pour l istace de taille À l étage e dessous se trouvet les a istaces de taille /b, à l étage ecore e dessous se trouvet les a 2 istaces de taille /b 2, etc Tout e bas de l arbre se trouvet les «cas de base» correspodat aux istaces de tailles iférieures à 0 Chaque istace cotribue à la complexité globale (via les étapes de divisios et de règes) à travers la foctio α Si a > b α, c est vers le bas que se trouvet les appels qui coûtet le plus cher C est par exemple le cas das l algorithme de Karatsuba Si a < b α, à l iverse, c est le haut de l arbre qui coûte cher Efi, si a = b α, tous les iveaux de l arbre cotribuet de maière équilibrée O verra lors de l étude des algorithmes de tris qu o peut trier u tableau de taille avec ue complexité O( l()), à l aide d ue stratégie «diviser pour réger» ayat u arbre d appels équilibré semblable à celui de la figure 5, chaque iveau cotribuat à la complexité de maière liéaire e 2 2 2 2 2 2 2 2 2 2 2 k 2 k 2 k 2 k Figure 5 U arbre d appels récursifs La preuve du théorème gééral est essetiellemet la même que celle de la complexité de l algorithme de Karatsuba : o déroule la récurrece jusqu aux cas termiaux O suppose que est ue puissace de b et s écrit doc b k Puisqu il existe deux costates A et B telles que C() ac(/b) + A α + B, o se retrouve avec la majoratio : C() a k C 0 + A α [1 + a b α + a2 b 2α + + ak 1 b ] + B ( 1 + a + a 2 + + a k 1) (k 1)α Svartz Page 10/12 2014/2015
O voit sur la somme partielle cetrale des ( a b α ) i apparaître la distictio : soit o se retrouve avec le terme gééral d ue série géométrique covergete (a < b α ), soit ue somme partielle dot le terme gééral ted vers l ifii (a > b α ), soit ue série dot le terme gééral est 1 car a = b α et dot la somme est k = log b () De même que pour l algorithme de Karatsuba, la complexité pour o ue puissace de b est borée par C(b log b () ) Coclusio sur la récursivité O a vu das ce cours les avatages et les icovéiets de la récursivité Icovéiets : Il faut faire attetio à e pas faire trop d appels récursifs imbriqués Il faut faire attetio à e pas faire plusieurs fois les mêmes calculs, sous peie de voir la complexité exploser Lorsque deux solutios sot équivaletes, l ue e récursif, l autre e itératif, il est e gééral préférable d utiliser la versio itérative Avatages : Das l esemble, il est plus facile de prouver u algorithme récursif que so équivalet itératif L écriture d u programme récursif est souvet plus claire (plus mathématique), que so équivalet itératif Parfois, et c est là où la récursivité est vraimet importate, il est pas du tout aisé de trasformer u algorithme récursif e algorithme itératif C est e gééral le cas des algorithmes «diviser pour réger» Svartz Page 11/12 2014/2015
Aexe : l algorithme de Karatsuba e Pytho def Karatsuba(P,Q): """P et Q deux polyômes, taille(p)=taille(q) (pas écessairemet ue puissace de 2) Les polyômes sot doés par la liste de leurs coefficiets, ordoés de 1 à x^{-1} O retoure la liste des coefficiets correspodat au produit P*Q""" =le(p) if ==1: retur([p[0]*q[0]]) m=//2 P0=P[:m] P1=P[m:] #P=P0+X^m P1 Q0=Q[:m] Q1=Q[m:] #Q=Q0+X^m Q1 T0=Karatsuba(P0,Q0) # de taille 2*m-1 T1=Karatsuba(P1,Q1) # de taille 2*(-m)-1 = 2*m-1 si pair, 2*m+1 si impair for i i rage(m): #o utilise P1 et Q1 pour e pas allouer de la mémoire supplémetaire P1[i]+=P0[i] Q1[i]+=Q0[i] T2=Karatsuba(P1,Q1) #de taille 2*(-m)-1 =2*m-1 si pair, 2*m+1 si impair for i i rage(2*m-1): T2[i]=T2[i]-T1[i]-T0[i] if %2==1: """si pair, les tableaux T0,T1,T2 ot meme taille, sio T1 et T2 sot de taille 2*m+1""" T2[-2]=T2[-2]-T1[-2] T2[-1]=T2[-1]-T1[-1] produit=[0]*(2*-1) # pour stocker le resultat PQ=P0*Q0+(P1*Q0+P0*Q1)*X^m+P1*Q1*X^2m for i i rage(2*m-1): produit[i]+=t0[i] produit[i+m]+=t2[i] # décalage correspodat à T2: X^m produit[i+2*m]+=t1[i] # décalage correspodat à T1: X^(2*m) if %2==1: produit[3*m-1]+=t2[2*m-1] produit[3*m]+=t2[2*m] produit[4*m-1]+=t1[2*m-1] produit[4*m]+=t1[2*m] retur(produit) Svartz Page 12/12 2014/2015