Chapitre 2 Exemples d`algorithmes itératifs et récursifs - IMJ-PRG

publicité
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
Téléchargement