Comprendre le retour arrière en C ++

12

J'ai une bonne compréhension de base des principes fondamentaux du C ++, j'ai également une compréhension du fonctionnement de la récursivité. Je suis tombé sur certains problèmes comme le problème classique des huit reines et la résolution d'un Sudoku avec Backtracking.

Je me rends compte que je suis assez perdu quand il s'agit de cela, je n'arrive pas à me faire une idée du concept de retourner dans la pile de récursivité et de recommencer afin de résoudre le problème. Cela semble facile avec un stylo et du papier mais quand il s'agit d'écrire du code pour cela, je ne sais pas comment commencer à attaquer ces problèmes.

Il serait utile qu'il y ait un tutoriel destiné aux débutants pour revenir en arrière ou s'il y avait un bon livre où cela était couvert. Si quelqu'un peut faire la lumière sur ce sujet ou me donner des liens vers des références décentes, je serais vraiment reconnaissant.

Et oui, je sais que ce serait plus facile dans les langages fonctionnels mais j'aimerais aussi comprendre l'implémentation dans les langages impératifs.

nikhil
la source
Je pense que c'est une bonne question, mais je pense qu'il serait préférable de souligner la demande de quelqu'un pour expliquer le retour en arrière par rapport à la demande de tutoriels ou d'autres ressources. Une sorte d'explication approfondie répond à une liste de références chaque jour.
Adam Lear
Ce serait parfait si quelqu'un pouvait donner une explication détaillée, mais cela ne me dérangerait pas non plus de lire les références. C'est juste que je ne sais pas par où commencer.
nikhil

Réponses:

9

... Je n'arrive pas à comprendre mon idée de retourner dans la pile de récursivité et de recommencer afin de résoudre le problème.

En reculant, vous ne recommencez pas. Au lieu de cela, vous parcourez toutes les options dans la situation actuelle.

Pensez à trouver une solution pour un labyrinthe. À un moment où vous avez deux chemins différents, vous essayez d'abord celui de gauche. Si celui de gauche ne vous mène pas à la sortie, vous revenez au point et essayez l'autre chemin. Voilà comment fonctionne le retour en arrière. Dans 8 Q et d'autres problèmes où le retour arrière peut être utilisé, la partie déroutante est dans le domaine du problème - comment itérer à travers vos options dans une situation donnée de manière déterministe.

EDIT : ce qui suit est un pseudo-code permettant de comprendre le retour en arrière.

# depending on the problem, backtracking is not necessarily calling the
# method itself directly. for now, let's just stick with the simple case.

def backtracking(state)
  option_list = state.get_all_options
  option_list.each {|option|
    state.apply option
    return resolved if state.is_resolved
    return resolved if backtracking(state) == resolved
    state.undo option
  }
  return not_resolved
end

Pour la question 8Q:

  • state.get_all_options renverrait une liste des positions possibles pour la prochaine reine
  • state.is_resolved testerait si toutes les reines sont sur le plateau et si elles sont bonnes les unes avec les autres.
  • state.apply et state.undo modifieront la carte pour appliquer ou annuler un positionnement.
Codisme
la source
Le premier code récursif que j'ai écrit (en 1984 en utilisant Pascal) pour une affectation était un algorithme de résolution de labyrinthe.
Gerry
Je connais une tâche simple où je peux réellement écrire du code pour obtenir la sensation réelle de ce genre de choses.
nikhil
@nikhil: demandez-vous s'il y a un problème simple? Il est préférable d'écrire du pseudo-code pour illustrer le routage générique du backtracking. J'essaierai un plus tard dans une réponse.
Codisme
Oui, ce sera très utile.
nikhil
Merci beaucoup, j'ai lu des trucs récemment. Lentement mais régulièrement, ma compréhension s'améliore.
nikhil
5

Vous avez vu un programme pour parcourir un arbre binaire, non? Cela ressemble à ceci:

void walk(node* p){
  if (p == NULL) return;  // this is backtracking
  else if (WeWin(p)){
    // print We Win !!
    // do a Throw, or otherwise quit
  }
  else {
    walk(p->left);   // first try moving to the left
    walk(p->right);  // if we didn't win, try moving to the right
                     // if we still didn't win, just return (i.e. backtrack)
  }
}

Voilà votre retour en arrière.

Vous n'avez pas vraiment besoin d'un arbre physique. Tout ce dont vous avez besoin est un moyen de faire un pas et de l'annuler plus tard, ou de dire si vous avez gagné, ou de dire si vous ne pouvez pas aller plus loin.

Mike Dunlavey
la source
1
ne pouvez-vous pas retourner un bool / int pour vérifier si la solution est trouvée dans le sous-arbre? else{return walk(p->left)||walk(p->right));}pas besoin de lancer pour le résultat escompté
reaket freak
@ratchet: Absolument. C'est aussi une excellente façon de procéder. (J'essayais juste de désencombrer l'exemple. Je le ferais en fait à votre façon.)
Mike Dunlavey
La découpe @MikeDunlavey est cependant assez importante dans la pratique.
jupp0r