Probléme : récursivité

Fermé
assma.m Messages postés 3 Date d'inscription vendredi 17 octobre 2008 Statut Membre Dernière intervention 22 janvier 2009 - 22 janv. 2009 à 18:48
Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 - 24 janv. 2009 à 23:25
Bonjour,
J'arrive pas à comprendre ce probléme qui porte sur récursiivité....
J'aime bien que vous m'aidez
voici c l'exercice:
Supposons qu'on a un carré de 10x10 cases, et qu'on veut noircir toutes ses cases; le remplissage ne s'effectuera pas en ligne ou en colonne mais en diagonale, de gauche à droite et du bas vers le haut.
Pour cela, si à un instant t on se trouve à la case de coordonnées (x,y), alors la prochaine case à noircir sera la case de coordonnées (x+1,y-1).

j'ai pas même compris la solution qui est la suivante:
procedure trace(x, y, x_droite, y_bas, total_posees: integer);
begin
tab[x, y] := '$';
afficher_tableau;
if (x >= dim) and (y >= dim) then exit; // point d'arrêt
if y = 1 then
begin
if x >= dim then inc(x_droite); // effet de bord
x := x_droite;
y := y_bas + 1;
if y > dim then dec(y); // effet de bord
y_bas := y;
if total_posees < dim * dim then // carré rempli ?
trace(x, y, x_droite, y_bas, total_posees + 1);
end
else
if x >= dim then
begin
if y_bas = dim then inc(x_droite); // effet de bord
x := x_droite; y := y_bas;
if total_posees < dim * dim then // carré rempli ?
trace(x, y, x_droite, y_bas, total_posees + 1);
end
else
if total_posees < dim * dim then // carré rempli ?
trace(x + 1, y - 1, x_droite, y_bas, total_posees + 1);
end;


je sais bien que ça vous prend beaucoup de temps de lire toute cette procédure. Aidez moi svp ; je peux pas résonner face à un probléme qui porte sur la récursivité, je peux pas choisir le module , les entrées et les sorties.
Je comprends pas pourquoi le correcteur a choisit: x,y, x_droite,y_bas, total_posees??? On peut pas résoudre le probléme en utilsant seulement x et y??
Une autre petite question: est ce qu'on peut utiliser ('exit') pour sortir de la procédure?? Comment on peut le traduire sur Pascal et C???
Merci d'avance

1 réponse

Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 125
24 janv. 2009 à 23:25
Salutations,

Tout d'abord je me permets de remettre le même code un peu mieux disposé.

procedure trace(x, y, x_droite, y_bas, total_posees: integer);
begin

	tab[x, y] := '$';

	afficher_tableau;

	if (x >= dim) and (y >= dim) then
		exit; // point d'arrêt

	if y = 1 then
	begin

		if x >= dim then
			inc(x_droite); // effet de bord

		x := x_droite;
		y := y_bas + 1;

		if y > dim then
			dec(y); // effet de bord

		y_bas := y;

		if total_posees < dim * dim then // carré rempli ?
			trace(x, y, x_droite, y_bas, total_posees + 1);
		end
	else
		if x >= dim then
		begin
			if y_bas = dim then
				inc(x_droite); // effet de bord

			x := x_droite;
			y := y_bas;

			if total_posees < dim * dim then // carré rempli ?
				trace(x, y, x_droite, y_bas, total_posees + 1);
			end
		else
			if total_posees < dim * dim then // carré rempli ?
				trace(x + 1, y - 1, x_droite, y_bas, total_posees + 1);
end;


Dans tout exercice de récursivité on retrouvera des cas d'arrêts et un ou des cas généraux. Le premier travail pour écrire ou comprendre un algorithme récursif est d'identifier ces deux parties. (On voit très bien ici pourquoi il faut commenter son code...)

1) Le cas général :
if ( y = 1 )	-> Pas ici
	...
else
	if ( x >= dim )	-> pas ici
		...
	else
		...	-> Il ne reste plus que ici


Cela ressemble d'ailleurs à l'énoncé : au tout début de la procédure un '$' est écrit pour noircir une case, puis on recommence avec x + 1 et y - 1. Le cas général est trouvé, à partir d'une case il trace la case suivante dans la diagonale.
Le rôle des variables est également très important, ici le cas général ne permet pas de comprendre le rôle de x_droite ou y_bas.

On remarque que ce cas général ne suffit pas à tracer toutes les diagonales mais une seule, le reste du code regroupe donc des cas d'arrêts et une autre partie qui sert à changer de diagonale à dessiner.

2) Les cas d'arrêts :
Pour une fois dans ce code, c'est commenté (Youpi !)

Premier cas d'arrêt :
if (x >= dim) and (y >= dim) then
	exit; // point d'arrêt
La case à dessiner est la dernière en bas à droite.

Autres cas d'arrêt :
if total_posees < dim * dim then // carré rempli ?
Si le nombre de cases dessinées n'est pas atteint on dessine une case en rappelant trace (on cherchera après la quelle) sinon, la fonction se termine sans se rappeler elle-même, donc fin de la récursivité.

Essayons de comprendre le reste :
Pour comprendre l'algorithme, une bonne façon de faire est de l'exécuter soi-même (avec une feuille et un crayon)

J'ai vu sur un site que le tout premier appel était trace ( 1, 1, 1, 1, 1 )

1)
tab[x, y] := '$';
La case x = 1, y = 1 est noircie.

2)
if y = 1 then
C'est vrai

3)
if x >= dim then
	inc(x_droite); // effet de bord
C'est faux, d'ailleurs ce cas ne sera vrai que pour la case la plus en haut à droite : y = 1 et x >= dim (ou en cas de dépassement vers la droite sur la première ligne)

4)
x := x_droite;
y := y_bas + 1;

if y > dim then
	dec(y); // effet de bord

y_bas := y;

x qui valait 1 reste 1 (x_droite)
y qui valait 1 devient 2
Si y dépasse la limite du tableau, on lui retire 1
y_bas devient 2 aussi.

5)
trace(x, y, x_droite, y_bas, total_posees + 1);

On trace la case suivante avec trace( 1, 2, 1, 2, 2 );

6)
Dans ce nouvel appel, y n'est pas 1 et x ne dépasse pas dim, le cas général est exécuté :
trace ( 2, 1, 1, 2, 3 )

7)
y = 1 : on peut remarquer dans le 4) que tant que x >= dim est faux et que y > dim est faux, ce morceaux de code ne va faire que incrémenter mettre y_bas + 1 dans y et x_droite dans x

Ici, le code va dessiner les premières diagonales en appelant successivement les procédures suivantes :
trace( 1, 1, 1, 1, 1 ) y = 1 -> première diagonale finie

trace( 1, 2, 1, 2, 2 ) -> cas général
trace( 2, 1, 1, 2, 3 ) y = 1 -> deuxième diagonale finie

trace( 1, 3, 1, 3, 4 ) -> cas général
trace( 2, 2, 1, 3, 5 ) -> cas général
trace( 3, 1, 1, 3, 6 ) y = 1 -> troisième diagonale finie

trace( 1, 4, 1, 4, 7 ) -> cas général
trace( 2, 3, 1, 4, 8 ) -> cas général
trace( 3, 2, 1, 4, 9 ) -> cas général
trace( 4, 1, 1, 4, 10 ) y = 1 -> quatrième diagonale finie

trace( 1, 5, 1, 5, 11 ) -> cas général
...

C'est de cette manière que j'ai compris l'utilisation de x_droite et y_bas : il donne le point de départ de la diagonale courante pour savoir quelle sera la suivante à dessiner.

Lorsque l'on dessine les diagonales les premières sont finies lorsqu'elles ont atteint la première ligne du tableau
Ensuite, dans la deuxième moitié du tableau, on sait quand une diagonale est finie lorsque celle-ci touche le bord droit du tableau. c'est ce qui est fait dans if x >= dim then

x_droit et y_bas vont donc désigner toutes les cases de la première colonne puis toutes celles de la dernière lignes.

On peut donc remarquer que l'auteur a manqué de logique :
lorsqu'il dessine la première moitié, celles qui sont au dessus de la plus grande diagonale, on ne peut pas avoir fini de dessiner tout le tableau, donc dans le code suivant :
if y = 1 then
begin
	...

	if total_posees < dim * dim then // carré rempli ?
		trace(x, y, x_droite, y_bas, total_posees + 1);

Le total de cases dessinées est obligatoirement plus petit que le nombre total de cases du tableau (dim * dim)

(sauf dans le cas où dim vaut 1)

L'auteur, afin de rendre son code plus illisible a choisi de faire systématiquement le même test avant tous les appels à la procédure récursive (if total_posees < dim * dim then // carré rempli ?) Il aurait suffit de faire ce test au début de la procédure. Il se serait rendu compte ce test est le même que celui qui est déjà fait (if (x >= dim) and (y >= dim) then) puisque avoir rempli le tableau jusqu'à x et y = dim revient à avoir le atteint le nombre total de cases. le dernier paramètre est donc inutile. (Je me serait attendu à faire le premier appel à la fonction avec les paramètres trace ( 1, 1, 1, 1, 0 ) puisque lorsque je demande de tracer le nombre de cases déjà dessinées est zéro, en tout cela me parait plus logique)

Je ne sais pas si j'ai bien expliqué ce que j'avais compris de ce code donc : J'écoute toutes tes questions ;-)

Je te laisse le soin de simplifier ce code et de le ré-écrire dans les autres langages. N'hésite surtout pas à poster ces codes en même temps que tes questions si tu veux notre avis sur ceux-ci.

Voili voilou, à bientôt,
M.
0