Chapitre 2 Exemples d'algorithmes itératifs et récursifs Dans ce chapitre on va mettre l'accent sur l'écriture des algorithmes et leur justication (l'algorithme se termine et produit le bon résultat). 2.1 Deux versions de l'algorithme d'Euclide Proposition 2.1.1 Soient : a ∈ Z, b ∈ Z. On a pour tout m ∈ Z : P GCD(a, b) = P GCD(b, a − mb) . On peut en particulier appliquer la proposition précédente au cas où m = q est le quotient dans la division euclidienne de a par b. Entrée: Deux entiers relatifs : a, b; Sortie: Un entier pgcd de a et b; Fonction PGCD(a, b) ; r ← a mod b (reste de la division euclidienne) ; si r est nul alors retourner sinon retourner b; PGCD(b, r); fsi Algorithme 1: Euclide, forme récursive : Deux entiers relatifs : a, b; Sortie: Un entier pgcd de a et b; Fonction PGCD(a, b) ; Entrée b est non nul faire r ← a mod b (reste de la division euclidienne) ; a ← b ;b ← r ; tantque ftantque retourner a; Euclide, forme impérative ou itérative On dispose aussi d'une variante binaire de cet algorithme, qui permet d'éviter les divisions en les remplaçant par des opérations de décalage. Algorithme 2: 1 : Deux entiers : a, b avec b ≥ 3 impair et a > 0; Sortie: Un entier pgcd de a et b; Fonction PGCD(a, b) ; Entrée tantque si a 6= b faire a>b alors a pair alors a ← a/2 ; (On pourra utiliser le décalage binaire); sinon si a← a−b ; 2 sinon b−a 2 b ← a ; a ← r; r← fsi fsi ftantque retourner a; Algorithme 3: Exercice 2.1.2 Euclide, version binaire impérative Améliorer l'algorithme binaire au cas b > 0 de parité quelconque. 2.2 Algorithme d'Euclide étendu aux coecients de Bezout Soient : a ∈ Z, b ∈ Z, d = P GCD(a, b). Il existe deux entiers u et v tels que Proposition 2.2.1 d = ua + vb. : 2 entiers a et b Sortie: Une liste de 3 entiers : (u, v, d) tels que a.u + v.b = d Fonction Bezout(a, b); A ← (1, 0, a) ; //On notera Ai le iieme opérande de la liste A ; B ← (0, 1, b) ; //On notera Bi le iieme opérande de la liste B ; reste ← b; Entrée tantque reste6=0 faire q ← le quotient de A3 par reste; temp ← A − q × B ; A ← B; B ← temp; reste ← B3 ; ftantque retourner A Algorithme 4: Euclide étendu : version impérative Programmez cet algorithme en Python et sous Xcas, en utilisant des listes. Achez le nombre de tours eectués. Exercice 2.2.2 La version récursive sous xcas avec la syntaxe de type algorithmique : (giac/xcas) fonction bezoutr (a , b) // r e n v o i e local si ( b!=0) // // la liste [u,v ,d] telle que a ∗ u+b ∗ v=d=p g c d ( a , b ) lb , q , r ; pour iquo alors des polynomes par quo et il irem suffit par de remplacer rem q := i q u o ( a , b ) ; 2 ( fct recursive ) r := i r e m ( a , b ) ; l b := b e z o u t r ( b , r ) ; retourne ( [ l b [ 1 ] , l b [ 0 ] + ( − q ) ∗ l b [ 1 ] sinon retourne ( [ 1 , 0 , a ] ) ; fsi ; ffonction ; , lb [ 2 ] ] ) ; exemple : [ a , b,d]:=bezoutr(1024,35); a∗1024+b∗35−d; ou avec la syntaxe traditionnelle (cf le menu exemples d'xcas) : (giac/xcas) bezoutr ( a , b):={ // r e n v o i e local if la liste [u,v ,d] telle que a ∗ u+b ∗ v=d=p g c d ( a , b ) ( fct recursive ) lb , q , r ; ( b!=0) { q := i q u o ( a , b ) ; r := i r e m ( a , b ) ; l b := b e z o u t r ( b , r ) ; return ( [ l b [ 1 ] , l b [0]+( −q )∗ lb [ 1 ] , lb [ 2 ] ] ) ; } else return ( [ 1 , 0 , a ] ) ; // } : ; // le :; permet de lorsqu ' i l ne pas n 'y a afficher q u ' une le ligne , programme pas apres besoin des l ' avoir {} valid \'e exemple : bezoutr(1024∗77,35∗404); Proposition 2.2.3 On considère deux entiers a, b et l'on pose : r0 = a et r1 = b. On dénit par récurrence tant que ri est non nul : ri+1 = ri−1 − qi .ri où qi est le quotient de la division Euclidienne de ri−1 par ri . 0n pose u0 = 1, u1 = 0 et v0 = 0, v1 = 1. Alors les suites (ui , vi ) dénies par ui+1 = ui−1 − qi .ui et vi+1 = vi−1 − qi .vi tant que ri est non nul, vérient la relation suivante : ui .a + vi .b = ri pour toute les valeurs de i où elles sont dénies. Notons N le nombre d'itérations de l'algorithme d'Euclide. Alors les suites (ui )N ≥i≥1 sont de signe alterné et (|ui |)N ≥i≥1 et (|vi |)N ≥i≥1 sont croissantes Exercice 2.2.4 et (vi )N ≥i≥1 2.3 Exemples de coûts Division euclidienne pour les entiers positifs Théorème 2.3.1 et 0 ≤ r < b. 2.3.1 Pour a ∈ IN, b ∈ IN∗ , il existe un unique couple d'entiers (q, r) avec a = bq + r Méthode naïve : : a ≥ 0 et b > 0 ; q ← 0 ; r ← a; tantque r > b faire r ← r − b; q ← q + 1; Entrée ftantque retourner q et r; Division euclidienne, méthode naïve Le nombre de boucle de cet algorithme est q ; à chaque étape il y a une soustraction et une comparaison ; pour b xé la complexité croît linéairement avec a. Algorithme 5: 3 Si on mesure la taille de l'entier a par le nombre de chires de son développement en binaire : t = log2 (a), alors la complexité croît exponentiellement avec la taille de a. Remarque 2.3.2 2.3.2 Recherche dichotomique : a ≥ 0 et b > 0 ; Sortie: Le quotient et le reste de la division de a par b n ← 0; Entrée 2n b ≤ a n ← n + 1; tantque faire ftantque α ← 2n−1 ;β ← 2n ; pour k de 1 jusque n − 1 α+β γ= ; 2 si γb ≤ a alors α ←γ; faire sinon β ←γ; fsi fpour retourner q = α et r = a − bq ; Algorithme 6: Exercice 2.3.3 2.3.3 Division euclidienne, recherche dichotomique Comparez avec la méthode que vous utilisez à la main. Puissance rapide Soit (G, ∗) un groupe commutatif dont on note la loi multiplicativement. On peut calculer an en O(log(n)) opérations ∗. Proposition 2.3.4 On distinguera deux situations assez diérentes selon que le coût de x ∗ y est indépendant des éléments x, y de G ou pas. Donnez 2 exemples de G dans chaque situation. Remarque 2.3.5 : Un élément g de G et un naturel n. : Un élément de G : g n Fonction puiss(g, n) ; u ← 1; v ← g ; //u,v : 2 variables locales ; Entrée Sortie tantque si n>1 faire n pair alors v ← v ∗ v ; n ← n/2; sinon u ← u ∗ v; v ← v ∗ v; n ← (n − 1)/2; fsi ftantque retourner u ∗ v; Algorithme 7: Exercice 2.3.6 de n. 2.3.4 Puissance rapide. Comparez cet algorithme avec un algorithme de recherche de l'écriture en base 2 Sujet 0 : Première épreuve d'amissibilité P̀o˘u˚rffl n ∈ IN ˚t´e¨l `qfi˚u`e n > 1, `o“nffl ”n`o˘t´e In ˜l„`e›n¯sfi`e›m˜b˝l´e `d`e˙s `é¨l´é›m`e›n˚t˙s ˚i‹n‹vfleˇr¯sfi˚i˜b˝l´e˙s `d`e ˜l„`a‹n‹n`e´a˚uffl (Z/nZ, +, ×). 4 ... 5. P̀o˘u˚rffl ˜l´e˙s `a˜l´g´o˘r˚i˚t‚h‹m`e˙s `d`e›m`a‹n`d`é˙s, `o“nffl ˚u˚tˇi˜lˇi¯sfi`eˇr`affl ˚u‹n˚i`qfi˚u`e›m`e›n˚t ˜l´es˙ `o¸p`éˇr`a˚tˇi`o“n¯s ×, +,ˆ `eˇt ˜l´affl ˜f´o“n`cˇtˇi`o“nffl `d`e `d`eˇu‹x ”vˆa˚r˚i`a˜b˝l´e˙s reste `o˘ùffl reste(a,b) `d`o“n‹n`e ˜l´e ˚r`e˙sfi˚t´e `d`e ˜l´affl `d˚i‹v˘i¯sfi˚i`o“nffl `eˇu`c¨lˇi`d˚i`e›n‹n`e `d`e `affl ¯p`a˚rffl ˜b ¯p`o˘u˚rffl a ∈ IN `eˇt b ∈ IN∗ . O”nffl ¯p`o˘u˚r˚r`affl `é´g´a˜l´e›m`e›n˚t ˚u˚tˇi˜lˇi¯sfi`eˇrffl `d`e˙s ˜bˆo˘u`c¨l´e˙s `d`e ˚t›y˙p`e - for - while - `eˇt ˜l´affl `c´o“n¯sfi˚tˇr˚u`cˇtˇi`o“nffl if...then...else... O”nffl ¯p˚r`é´cˇi¯sfi`eˇr`affl ˜l´e ˜l´oˆgˇi`cˇi`e¨l `d`e `c´a˜l´cˇu˜l ˜f´o˘r‹m`e¨l `o˘uffl ˜l´e ”m`oˆd`è¨l´e `d`e `c´a˜l´cˇu˜l´a˚tˇr˚i`c´e ˚u˚tˇi˜lˇi¯sfi`é. 5.1. É`cˇr˚i˚r`e ˚u‹n`e ¯p˚r`oˆc´é´d˚u˚r`e Test( , ) `a‹y´a‹n˚t `c´o“m‹m`e `a˚r`gˇu‹m`e›n˚t˙s `d`eˇu‹x `e›n˚tˇi`eˇr¯s ”n`a˚tˇu˚r`e¨l˙s ˛kffl `eˇt ”nffl `a‹vfle´c n > 1 `a˜f¨fˇi`c‚h`a‹n˚t ‘‘1’’ ¯sfi˚iffl k ∈ In `eˇt ‘‘0’’ ¯sfi˚i‹n`o“nffl. 5.2. É`cˇr˚i˚r`e ˚u‹n`e ¯p˚r`oˆc´é´d˚u˚r`e Card( ) `a‹y´a‹n˚t `c´o“m‹m`e `a˚r`gˇu‹m`e›n˚t ˚u‹nffl `e›n˚tˇi`eˇrffl n `a‹vfle´c n > 1 `a˜f¨fˇi`c‚h`a‹n˚t ˜l´e `c´a˚r`d˚i‹n`a˜l `d`e In . 5.3. É`cˇr˚i˚r`e ˚u‹n`e ¯p˚r`oˆc´é´d˚u˚r`e Ord( , ) `a‹y´a‹n˚t `c´o“m‹m`e `a˚r`gˇu‹m`e›n˚t˙s `d`eˇu‹x `e›n˚tˇi`eˇr¯s ”n`a˚tˇu˚r`e¨l˙s k `eˇt n `a‹vfle´c n > 1 `a˜f¨fˇi`c‚h`a‹n˚t ˜l´affl ”vˆa˜l´eˇu˚rffl `d`e ω(k) , ˜l„`o˘r`d˚r`e `d`e k `d`a‹n¯s (In , ×), ¯sfi˚iffl k ∈ In `eˇt "E˚r˚r`eˇu˚r" ¯sfi˚i‹n`o“nffl. 5 TP02 Exercice I: 1) a) Programmez l'algorithme du pgcd récursif en python. (on créera une fonction gcd en python. b) Mettez aussi dans votre chier des valeurs assez grandes d'entiers et testez votre fonction. Vériez vos résultats avec xcas. (Trouvez la bonne commande dans les menus déroulants) 2) a) Créez en python une fonction bezout selon l'algorithme de bezout itératif. b) Dans xcas, trouvez l'instruction correspondante déjà implémentée, et vériez votre programme. Exercice II: 1) En vous inspirant de l'exercice VI du TP01, créez une classe ZN dont les éléments seront initialisés et achés comme dans l'exemple ci dessous : (python) >>> m=ZN ( 1 3 , 7 ) >>> m ' Classe de 13 modulo 7 ' 2) Ajoutez à votre classe une méthode __eq__ pour comparer ZN(a,b) et ZN(c,d) de la facon suivante : retour True si b = d et a et b sont congrus modulo b, sinon __eq__ doit retourner False 3) a) Ajoutez a votre classe les méthodes __add__ et __mul__ pour dénir la structure d'anneau de Z/nZ. (On simpliera le représentant) 4) Dénir une méthode __div__ pour la division. On retournera None si cette division est impossible. 5) Vériez vos résultats avec xcas. Exercice III: Etudiez le sujet 0. 1) a) Programmez rapidement les questions 5.1 et 5.2. méthode naive pour 5.2. b) Si n est un produit de 2 nombres premiers, que vaut le cardinal de In ? Quel est le nombre de tours de votre méthode ? c) Quel est le nom mathématique de cette fonction ? Quelle est l'instruction xcas correspondant à cette fonction ? 2) Programmez le 5.3 de manière naive. Exercice IV: 1) Créez en Python une fonction puis(a,n) qui calcule an par puissances rapides. 2) On pose p = 1021 + 117. a) Vériez avec xcas que c'est un nombre premier. p−1 p−1 b) Calculez dans Z/pZ les nombres : 5p−1 , 5 2 , 7 2 . Interprétation ? Exercice V: Programmez en python une fonction qui retourne l'écriture en base 2 d'un entier. (Sous forme d'une liste d'entiers) 6