J'ai étudié les fonctions récursives, et apparemment, ce sont des fonctions qui s'appellent elles-mêmes et n'utilisent pas d'itérations/boucles (sinon ce ne serait pas une fonction récursive).
Cependant, en surfant sur le Web pour des exemples (le problème récursif des 8 reines), j'ai trouvé cette fonction:
private boolean placeQueen(int rows, int queens, int n) {
boolean result = false;
if (row < n) {
while ((queens[row] < n - 1) && !result) {
queens[row]++;
if (verify(row,queens,n)) {
ok = placeQueen(row + 1,queens,n);
}
}
if (!result) {
queens[row] = -1;
}
}else{
result = true;
}
return result;
}
Il y a une boucle while
impliquée.
... donc je suis un peu perdu maintenant. Puis-je utiliser des boucles ou non?
Vous avez mal compris la récursivité: bien qu'elle puisse être utilisée pour remplacer l'itération, il n'y a absolument aucune obligation pour la fonction récursive de ne pas avoir d'itérations internes à elle-même.
La seule condition pour qu'une fonction soit considérée comme récursive est l'existence d'un chemin de code par lequel elle s'appelle, directement ou indirectement. Toutes les fonctions récursives correctes ont également un conditionnel quelconque, les empêchant de "se reproduire" pour toujours.
Votre fonction récursive est idéale pour illustrer la structure de la recherche récursive avec retour arrière. Cela commence par la vérification de la condition de sortie row < n
, et procède à des décisions de recherche sur son niveau de récursivité (c'est-à-dire choisir une position possible pour le nombre de reines row
). Après chaque itération, un appel récursif est effectué pour s'appuyer sur la configuration que la fonction a trouvée jusqu'à présent; finalement, il "touche le fond" lorsque row
atteint n
dans l'appel récursif qui est n
profond.
La structure générale d'une fonction récursive ressemble à ceci:
myRecursiveFunction(inputValue)
begin
if evaluateBaseCaseCondition(inputValue)=true then
return baseCaseValue;
else
/*
Recursive processing
*/
recursiveResult = myRecursiveFunction(nextRecursiveValue); //nextRecursiveValue could be as simple as inputValue-1
return recursiveResult;
end if
end
Le texte que j'ai marqué comme /*recursive processing*/
pourrait être n'importe quoi. Il pourrait inclure une boucle, si le problème résolu le nécessite, et pourrait également inclure des appels récursifs à myRecursiveFunction
.
Vous pouvez sûrement utiliser des boucles dans une fonction récursive. Ce qui rend une fonction récursive, c'est seulement le fait que la fonction s'appelle à un moment donné de son chemin d'exécution. Cependant, vous devriez avoir une condition pour empêcher les appels de récursion infinis à partir desquels votre fonction ne peut pas retourner.
Les appels et boucles récursifs ne sont que deux manières/constructions pour implémenter un calcul itératif.
Une boucle while
correspond à un appel récursif de queue (voir par exemple ici ), c'est-à-dire une itération dans laquelle vous n'avez pas besoin de sauvegarder les résultats intermédiaires entre deux itérations (tous les résultats d'une cycle sont prêts lorsque vous entrez dans le cycle suivant). Si vous avez besoin de stocker des résultats intermédiaires que vous pourrez réutiliser plus tard, vous pouvez soit utiliser une boucle while
avec une pile (voir ici ), soit une boucle non récursive (c'est-à-dire arbitraire ) appel récursif.
De nombreuses langues vous permettent d'utiliser les deux mécanismes et vous pouvez choisir celui qui vous convient le mieux et même les mélanger dans votre code. Dans les langages impératifs comme C, C++, Java, etc., vous utilisez normalement une boucle while
ou for
lorsque vous n'avez pas besoin d'une pile, et vous utilisez des appels récursifs lorsque vous avez besoin d'une pile (vous utiliser implicitement la pile d'exécution). Haskell (un langage fonctionnel) n'offre pas de structure de contrôle d'itération, vous ne pouvez donc utiliser que des appels récursifs pour effectuer l'itération.
Dans votre exemple (voir mes commentaires):
// queens should have type int [] , not int.
private boolean placeQueen(int row, int [] queens, int n)
{
boolean result = false;
if (row < n)
{
// Iterate with queens[row] = 1 to n - 1.
// After each iteration, you either have a result
// in queens, or you have to try the next column for
// the current row: no intermediate result.
while ((queens[row] < n - 1) && !result)
{
queens[row]++;
if (verify(row,queens,n))
{
// I think you have 'result' here, not 'ok'.
// This is another loop (iterate on row).
// The loop is implemented as a recursive call
// and the previous values of row are stored on
// the stack so that we can resume with the previous
// value if the current attempt finds no solution.
result = placeQueen(row + 1,queens,n);
}
}
if (!result) {
queens[row] = -1;
}
}else{
result = true;
}
return result;
}
Vous avez raison de penser qu'il existe une relation entre la récursivité et l'itération ou la boucle. Les algorithmes récursifs sont souvent convertis manuellement ou même automatiquement en solutions itératives à l'aide de l'optimisation des appels de queue.
Dans huit reines, la partie récursive est liée au stockage des données nécessaires au suivi arrière. Lorsque vous pensez à la récursivité, il est utile de penser à ce qui est poussé sur la pile. La pile peut contenir des paramètres de passage par valeur et des variables locales qui jouent un rôle clé dans l'algorithme, ou parfois des éléments qui ne sont pas aussi apparemment pertinents comme l'adresse de retour ou dans ce cas, une valeur transmise avec le nombre de reines utilisées mais pas changé par l'algorithme.
L'action qui se produit dans huit reines est qu'essentiellement, on nous donne une solution partielle pour un certain nombre de reines dans les premières colonnes à partir desquelles nous déterminons de manière itérative des choix valides jusqu'à présent dans la colonne actuelle que nous passons récursivement pour être évalués pour le colonnes restantes. Localement, huit reines gardent une trace de la ligne qu'il essaie et si le suivi arrière se produit, il est prêt à parcourir les lignes restantes ou à revenir en arrière en retournant simplement s'il ne trouve aucune autre ligne qui pourrait fonctionner.
La partie "créer une version plus petite du problème" peut avoir des boucles. Tant que la méthode s'appelle en passant comme paramètre la version plus petite du problème, la méthode est récursive. Bien sûr, une condition de sortie, lorsque la plus petite version possible du problème est résolue et que la méthode renvoie une valeur, doit être fournie pour éviter un état de dépassement de pile.
La méthode de votre question est récursive.
La récursion appelle à nouveau votre fonction et le principal avantage de la récursivité est d'économiser de la mémoire. La récursivité peut contenir des boucles, elles sont utilisées pour effectuer une autre opération.