Documente Academic
Documente Profesional
Documente Cultură
1/39
Une fonction rcursive est une fonction qui peut sappeler elle-mme.
paramtres
retour
2/39
3/39
Cette fonction retourne n! si n est positif ou nul, et ne termine pas si n est ngatif (appels
rcursifs infinis).
Note : concrtement, si n est ngatif le programme provoque (le plus souvent) une erreur
lexcution pour cause de dpassement de mmoire autoris dans les appels de fonctions
(pile dappel).
4/39
Les variables a et b sont redclares chaque appel. Ceci est bien entendu galement vrai
pour les paramtres.
Algorithmique et programmation (Licence 1 - S2)
5/39
6/39
Fonctionnement de la rcursivit
Lappel des fonctions fonctionne laide dune pile dexcution qui conserve les contextes
dappel :
1. chaque appel de fonction, on empile le contexte : lieu de lappel, variables locales,
etc...
2. chaque retour de fonction, on dpile le contexte, ce qui permet de revenir au point
dappel.
(1)void fonc1(int a) { (2) (3)
int main() {
(0)
Pile dexecution
if (a>0)
l. 8 fonc1(a1)
main, l.21
fonc1, l.8
a=2
main, l.21
fonc1, l.8
a=1
fonc1, l.8
a=2
main, l.21
(4) (5)
(6) }
(0)
(1)
( debut )
(appel de
fonc1(2) )
(6)
(5)
(2)
(appel de
fonc1(1) )
(3)
(appel de
fonc1(0) )
(4)
( retour
( retour
( retour
de fonc1(2) ) de fonc1(1) ) de fonc1(0) )
7/39
8/39
while (c) {
/* ... bloc ... */
}
void fonctionrec() {
if ( !c) {
/* ... bloc ... */
fonctionrec() ;
}
}
Rciproque : tout algorithme itratif peut tre transform en algorithme rcursif sans
boucle (en grant convenablement les variables). Mais :
a peut tre moins lisible ;
chaque itration prend un peu de place en mmoire (occupe la pile) ;
la gestion des variables peut tre plus complique.
Pour des algorithmes de nature diffrente (par exemple les calculs de Fibonacci itratifs
et rcursifs vus en cours/TD), la complexit peut tre diffrente. Il est trs facile de faire
des algorithmes de complexit exponentielle avec la rcursivit.
Algorithmique et programmation (Licence 1 - S2)
9/39
10/39
Position initiale
Algorithmique et programmation (Licence 1 - S2)
Position finale
11/39
Principe de rsolution
Pour dplacer le disque le plus grand de la 1re la 3me tour, il faut que tous les autres
disques soient sur la deuxime tour :
Donc :
1. on dplace les n 1 disques plus petits de la 1re la 2me tour ;
2. on dplace le plus grand disque de la 1re la 3me tour ;
3. puis on dplacer les n 1 disques de la 2me la 3me tour.
Les tapes 1 et 3 se font par appel rcursif, en changeant le numro des tours.
Algorithmique et programmation (Licence 1 - S2)
12/39
Algorithme
fonction hanoi (nb_disques, depart, destination, intermediaire)
debut
si nb_disques = 1 alors
dplacer le disque 1 de depart vers destination
sinon
hanoi (nb_disques -1, depart, intermediaire, destination)
dplacer le disque nb_disques de depart vers destination
hanoi (nb_disques -1, intermediaire, destination, depart)
finsi
fin
13/39
Programme
Lalgorithme ne prcise pas ce que signifie dplacer . Si on ne veut pas reprsenter (en
mmoire) les tours, le plus simple est dafficher les actions faire.
void hanoi (int nb_disques, int depart, int destination, int intermediaire) {
if (nb_disques == 1) {
printf ("Dplacer le disque 1 de la tour %d la tour %d.\n",
depart, destination);
} else {
hanoi (nb_disques-1, depart, intermediaire, destination);
printf ("Dplacer le disque %d de la tour %d la tour %d.\n",
nb_disques, depart, destination);
hanoi (nb_disques-1, intermediaire, destination, depart);
}
}
14/39
le
le
le
le
le
le
le
disque
disque
disque
disque
disque
disque
disque
1
2
1
3
1
2
1
de
de
de
de
de
de
de
la
la
la
la
la
la
la
tour
tour
tour
tour
tour
tour
tour
1
1
3
1
2
2
1
la
la
la
la
la
la
la
tour
tour
tour
tour
tour
tour
tour
3.
2.
2.
3.
1.
3.
3.
(4)
1
2
3
hanoi(3,1,3,2)
(2)
1
(1)
2
3
hanoi(1,1,3,2)
2
3
hanoi(2,1,2,3)
(6)
1
(3)
1
2
3
hanoi(1,3,2,1)
(5)
2
3
hanoi(2,2,3,1)
2
3
hanoi(1,2,1,3)
(7)
2
3
hanoi(1,1,3,2)
15/39
Notes
1. Le schma de la page prcdente est un arbre binaire (chaque nud a 2 enfants).
En informatique, les arbres se reprsentent vers le bas.
2. On dit que cet arbre est parcouru en profondeur lorsquon descend le plus vite
possible vers les feuilles. La rcursivit est bien adapte pour crire un tel parcours.
3. Lordre de lecture fils gauche nud fils droit est appel lordre infixe.
4. La rcursivit permet ici :
de garder en rserve la partie droite pendant quon soccupe de la partie gauche ;
de conserver les valeurs locales de depart, destination, intermerdiaire.
Un tel algorithme serait difficile crire sans rcursivit.
16/39
Algorithme itratif
fonction hanoi_it (nb_disques, depart, destin, interm)
debut
p creer_pile()
empiler(p,nb_disque), empiler(p,depart), empiler(p,destin), empiler(p, interm)
tantque (non estVide(p)) faire
depiler(p,inter), depiler (p,dest), depiler (p,dep), depiler(p,nb)
si nb = 1 ou inter=0 alors
dplacer le disque nb de depart vers destination
sinon
empiler (p,nb-1), empiler(p,inter), empiler(p,dest), empiler(p,dep)
empiler (p,nb), empiler(p,dep), empiler(p,dest), empiler(p,0)
empiler (p,nb-1), empiler(p,dep), empiler(p,inter), empiler(p,dest)
finsi
fintantque
fin
Algorithmique et programmation (Licence 1 - S2)
17/39
Complexit
Combien dtapes sont ncessaires pour rsoudre le problme avec n disques ?
Si on note un ce nombre dtapes, on a :
1
un =
u
n1
si n = 1 (cas de base)
+ 1 + un1
si n > 1
18/39
Exemple 2 : le labyrinthe
Donne du problme
Un robot se trouve dans un labyrinthe (avec des couloirs et des murs), et cherche
rcuprer un objet.
O
R
R : robot
O : objet
Le robot ne peut que sentir ce quil y a autour de lui. Il peut se dplacer dans nimporte
quelle direction.
Algorithmique et programmation (Licence 1 - S2)
19/39
Reprsentation du labyrinthe
Le labyrinthe peut se reprsenter laide dune matrice (tableau deux dimensions). Par
exemple, une case avec 0 reprsente un mur, une case avec 1 reprsente un couloir, et une
case avec 2 reprsente lobjet.
int labyrinthe[11][15] =
{ { 0, 0, 0, 0, 0, 0, 0,
{ 0, 1, 1, 1, 0, 1, 1,
{ 0, 1, 0, 1, 1, 1, 0,
{ 0, 1, 0, 0, 1, 0, 0,
{ 0, 1, 1, 0, 1, 1, 0,
{ 0, 0, 1, 0, 0, 1, 1,
{ 0, 1, 1, 1, 0, 0, 1,
{ 0, 1, 0, 1, 1, 0, 1,
{ 0, 1, 0, 0, 0, 0, 1,
{ 0, 1, 0, 1, 1, 1, 1,
{ 0, 0, 0, 0, 0, 0, 0,
0,
1,
0,
1,
1,
1,
0,
0,
0,
1,
0,
0,
1,
0,
1,
0,
0,
0,
1,
1,
1,
0,
0,
1,
0,
0,
0,
1,
0,
1,
0,
0,
0,
0,
1,
1,
1,
1,
1,
0,
1,
1,
1,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
1,
0,
1,
1,
1,
0,
1,
0,
1,
0,
0,
1,
0,
2,
0,
1,
1,
1,
0,
1,
0,
0
0
0
0
0
0
0
0
0
0
0
},
},
},
},
},
},
},
},
},
},
} };
20/39
Principe de la recherche
La rcursivit permet dexplorer plusieurs choix ( gauche ? droite ? tout droit ?). Un
cas de base est donc quand il ny a plus de choix.
A priori, deux cas de bases :
1. le robot est dj lemplacement de lobjet : rien faire.
2. le robot est dans un cul-de-sac (cest--dire, la seule possibilit est de revenir sur ses
pas).
Sinon, le robot explore les diffrentes possibilits (appel rcursif). Si une possibilit russit
(mne lobjet), il ne doit pas continuer. Si toutes les possibilits chouent, il revient en
arrire.
21/39
Algorithme
La fonction doit retourner un boolen, pour savoir si on a trouv ou non lobjet.
fonction explore (pos_l, pos_c, orig_l, orig_c) retourne boolen
debut
aller en (pos_l, pos_c)
ok <- (labyrinthe[pos_l][pos_c]=2)
si (pas ok) et (pos_l -1 6= orig_l) et (labyrinthe[pos_l-1][pos_c]>0) alors
ok <- explore (pos_l -1, pos_c, pos_l, pos_c)
si (pas ok) et (pos_l+1 6= orig_l) et (labyrinthe[pos_l+1][pos_c]>0) alors
ok <- explore (pos_l +1, pos_c, pos_l, pos_c)
si (pas ok) et (pos_c+1 6= orig_c) et (labyrinthe[pos_l][pos_c+1]>0) alors
ok <- explore (pos_l, pos_c+1, pos_l, pos_c)
si (pas ok) et (pos_c-1 6= orig_c) et (labyrinthe[pos_l][pos_c-1]>0) alors
ok <- explore (pos_l, pos_c-1, pos_l, pos_c)
si (pas ok) alors aller en (orig_l, orig_c)
retourner ok
fin
22/39
Notes
1. Cet algorithme nest pas optimal (trop de tests rpts), mais est plus lisible (sur un
transparent).
2. Conditions de fonctionnement :
le labyrinthe doit tre entour de murs (sinon il faut tester que pos_x ou pos_y ne
sorte pas des limites du tableau) ;
le labyrinthe ne doit pas avoir de cycles, sinon lalgorithme peut boucler. On
regardera ce problme ensuite.
23/39
Traduction en C
int explore (int posl, int posc, int origl, int origc) {
int ok;
printf("Aller en %d,%d\n", posl, posc);
ok = (labyrinthe[posl][posc]==2);
if (!ok) {
if ((posl-1!=origl) && (labyrinthe[posl-1][posc]>0))
ok = explore(posl-1,posc,posl,posc);
if (!ok) {
if ((posl+1!=origl) && (labyrinthe[posl+1][posc]>0))
ok = explore(posl+1,posc,posl,posc);
if (!ok) {
if ((posc+1!=origc) && (labyrinthe[posl][posc+1]>0))
ok = explore(posl,posc+1,posl,posc);
if (!ok) {
if ((posc-1!=origc) && (labyrinthe[posl][posc-1]>0))
ok = explore(posl,posc-1,posl,posc);
if (!ok)
printf("Retour en %d,%d\n", origl, origc);
} } } }
return ok;
}
Algorithmique et programmation (Licence 1 - S2)
24/39
Petit problme pour lorigine lors du premier appel : on peut par exemple modifier la
fonction dexploration pour ne pas afficher de retour si lorigine est (-1,-1).
25/39
Aller
Aller
Aller
Aller
Aller
Aller
Aller
Aller
Aller
Aller
Aller
Aller
Aller
Aller
Aller
Aller
Aller
Aller
Aller
Aller
Aller
Aller
en
en
en
en
en
en
en
en
en
en
en
en
en
en
en
en
en
en
en
en
en
en
6,2
5,2
4,2
4,1
3,1
2,1
1,1
1,2
1,3
2,3
2,4
3,4
4,4
4,5
5,5
5,6
6,6
7,6
8,6
9,6
9,7
9,8
Aller en 8,8
Aller en 7,8
Aller en 7,9
Aller en 7,10
Aller en 8,10
Aller en 9,10
Aller en 9,11
Aller en 9,12
Aller en 9,13
Retour en 9,12
Retour en 9,11
Retour en 9,10
Retour en 8,10
Retour en 7,10
Retour en 7,9
Retour en 7,8
Retour en 8,8
Retour en 9,8
Retour en 9,7
Retour en 9,6
Aller en 9,5
Aller en 9,4
Aller en 9,3
Retour en 9,4
Retour en 9,5
Retour en 9,6
Retour en 8,6
Retour en 7,6
Retour en 6,6
Retour en 5,6
Aller en 5,7
Aller en 4,7
Aller en 3,7
Aller en 3,8
Retour en 3,7
Retour en 4,7
Retour en 5,7
Retour en 5,6
Retour en 5,5
Retour en 4,5
Retour en 4,4
Retour en 3,4
Retour en 2,4
Aller en 2,5
Aller en 1,5
Aller en 1,6
Aller en 1,7
Aller en 1,8
Aller en 1,9
Aller en 1,10
Aller en 2,10
Aller en 3,10
Aller en 4,10
Aller en 5,10
Aller en 5,9
Retour en 5,10
Retour en 4,10
Aller en 4,11
Aller en 4,12
Aller en 3,12
Aller en 3,13
Le robot a trouv lobjet !
O
R
R : robot
R
R : robot
O : objet
R
O : objet
R : robot
O : objet
R : robot
O : objet
26/39
Notes
1. L encore, on a une exploration en profondeur , classique des algorithmes rcursifs.
2. Le parcours exact dpend bien entendu de lordre des choix (entre c+1, c-1, l+1, l-1),
a priori compltement arbitraire.
3. La complexit de lalgorithme est limit par la taille totale du labyrinthe.
4. Lintrt de la rcursivit est de mmoriser le chemin parcouru pour pouvoir revenir
en arrire.
5. Un cycle peut provoquer une boucle infinie (et une erreur lexcution). Par exemple :
27/39
Ici cette valeur est bien adapte au reste des tests (>0 pour dsigner un couloir non explor
ou lobjet).
28/39
Exemple 3 : le morpion
Donne du problme
Trouver le coup jouer au morpion (sur une grille 3x3), en fonction de la situation actuelle.
29/39
Reprsentation du jeu
L encore, on peut utiliser une matrice (3x3). Par exemple, 0 est une case vide, 1 est une
croix, (-1) est un rond.
int jeu[3][3] = { { 0,0,0 }, { 0,0,0 }, { 0,0,0 } };
30/39
31/39
32/39
Principe du calcul
Deux cas de bases :
1. si le score linstant du jeu vaut 1 ou -1, on retourne ce score ;
2. si le score linstant vaut 0 et que le jeu est plein, on retourne 0.
Dans les autres cas, on teste les coups possibles pour le joueur actuel (appels rcursifs) :
1. si cest le joueur 1, on retourne le maximum des scores obtenu par chaque coup :
un coup qui donne un score de 1 permet dassurer que le joueur 1 peut forcer la
victoire ;
un coup qui donne un score de 0 assure au moins de ne pas perdre ;
seulement si tous les coups sont -1, on doit retourner -1.
2. si cest le joueur -1, on retourne le minimum des scores obtenus.
Cette alternance de calculs minimum/maximum est appel algorithme minmax (ou
minimax).
Algorithmique et programmation (Licence 1 - S2)
33/39
Fonction en C
int minimax (int position[3][3], int joueur) {
int resultat, i, j, resultat_coup, coups_possibles =0;
resultat = score (position);
if (resultat==0) {
resultat = -joueur; /* pire cas */
for (i=0;i<3;i=i+1) {
for (j=0;j<3;j=j+1) {
if (position[i][j]==0) {
position[i][j]=joueur;
/*
resultat_coup = minimax (position, -joueur); /*
position[i][j]=0;
/*
coups_possibles=1;
if (resultat_coup*joueur > resultat*joueur) {
resultat = resultat_coup;
}
}
}
}
if (coups_possibles==0) resultat = 0; /* jeu bloqu =
}
return resultat;
}
Algorithmique et programmation (Licence 1 - S2)
coup "virtuel" */
appel rcursif */
retour arrire */
match nul */
34/39
Utilisation
En modifiant lgrement le code, on peut mmoriser le (ou un) meilleur coup possible et
le jouer.
/* on suppose que la position nest pas finale */
void jouer (int position[3][3], int joueur) {
int resultat, i, j, resultat_coup, i_mieux, j_mieux;
resultat = -2*joueur; /* pour tre sur de mmoriser au
for (i=0;i<3;i=i+1) {
for (j=0;j<3;j=j+1) {
if (position[i][j]==0) {
position[i][j]=joueur;
/*
resultat_coup = minimax (position, -joueur); /*
position[i][j]=0;
/*
if (resultat_coup*joueur > resultat*joueur) {
resultat = resultat_coup;
i_mieux = i;
j_mieux = j;
}
}
} }
position[i_mieux][j_mieux]=joueur;
printf ("Le joueur %d joue en %d,%d.",joueur, i_mieux,
}
Algorithmique et programmation (Licence 1 - S2)
moins un coup */
coup "virtuel" */
calcul score */
retour arrire */
j_mieux);
35/39
Programme principal
On fait jouer un humain contre lordinateur (qui commence) :
int main () {
int joueur = 1, nb_libre = 9, score_actuel = 0;
while ((nb_libre>0) && (score_actuel==0)) {
if (joueur==1) { /* ordinateur */
jouer (jeu, 1);
} else {
/* joueur humain */
int i,j;
printf("Entrez votre coup : ");
scanf("%d",&i);
scanf("%d",&j);
jeu[i][j]=-1;
/* trs mauvais : aucun test derreur */
}
score_actuel = score (jeu);
nb_libre = nb_libre - 1;
joueur = -joueur;
}
if (score_actuel ==0) printf ("Match nul !\n");
else if (score_actuel ==1) printf("Lordinateur a gagn !\n");
else printf("Vous avez gagn (et trich).\n");
return 0;
}
Algorithmique et programmation (Licence 1 - S2)
36/39
Excution
Le joueur 1 joue en 0,0.
Entrez votre coup : 2 2
Le joueur 1 joue en 0,2.
Entrez votre coup : 0 1
Le joueur 1 joue en 2,0.
Entrez votre coup : 1 0
Le joueur 1 joue en 1,1.
Lordinateur a gagn !
37/39
Notes
1. Lordinateur joue parfaitement, sans aucun hasard ni intelligence . Tant quil nest
pas sr de pouvoir gagner, il ne cherche assurer que le match nul (en supposant son
adversaire de mme niveau).
2. La technique utilise est (encore) une exploration en profondeur de lensemble des
parties possibles (soit, initialement, 255 168 parties).
3. La complexit de lalgorithme dpend du nombre de combinaisons possibles modr
pour un jeu comme le morpion 3x3.
Lalgorithme du minmax, avec des optimisations (voir lagage alphabta), est la base
des joueurs automatiques dans les jeux de stratgies classiques (sans hasard).
38/39
39/39