Comprensione del backtracking in C ++


12

Ho una buona conoscenza di base dei fondamenti del C ++, ho anche una comprensione di come funziona la ricorsione. Mi sono imbattuto in alcuni problemi come il classico problema delle otto regine e la risoluzione di un Sudoku con Backtracking.

Mi rendo conto che sono abbastanza perso quando si tratta di questo, non riesco a riuscire a concentrarmi sul concetto di tornare nello stack di ricorsione e ricominciare per risolvere il problema. Sembra facile con carta e penna ma quando si tratta di scrivere codice per questo, sono confuso su come iniziare ad attaccare questi problemi.

Sarebbe utile se ci fosse un tutorial rivolto ai principianti al backtracking o se ci fosse un buon libro in cui questo è stato trattato. Se qualcuno può fare luce su questo argomento o darmi qualche link a riferimenti decenti, sarei davvero grato.

E sì, lo so che sarebbe più facile nei linguaggi funzionali, ma mi piacerebbe capire anche l'implementazione nei linguaggi imperativi.


Penso che questa sia una buona domanda, ma penso che sarebbe meglio sottolineare la richiesta di qualcuno che spieghi il backtracking chiedendo tutorial o altre risorse. Una spiegazione approfondita di tipo di risposta batte un elenco di riferimenti ogni giorno.
Adam Lear

Sarebbe perfetto se qualcuno potesse dare una spiegazione dettagliata, ma non mi dispiacerebbe nemmeno leggere i riferimenti. È solo che non so da dove cominciare.
nikhil,

Risposte:


9

... Non riesco a pensare al concetto di tornare nello stack di ricorsione e ricominciare per risolvere il problema.

Nel backtracking, non si ricomincia. Invece, si esegue l'iterazione di tutte le opzioni nella situazione corrente.

Pensa a trovare una soluzione per un labirinto. Ad un certo punto in cui hai due percorsi diversi, prova prima quello a sinistra. Se quello di sinistra non ti porta all'uscita, torni al punto e prova l'altro percorso. Ecco come funziona il backtracking. In 8 Q e altri problemi in cui è possibile utilizzare il backtracking, la parte confusa è nel dominio del problema: come scorrere le opzioni in una determinata situazione in modo deterministico.

EDIT : il seguente è uno pseudo codice che aiuta a comprendere il backtracking.

# 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

Per domanda 8Q:

  • state.get_all_options restituirebbe un elenco delle posizioni possibili per la regina successiva
  • state.is_resolved verificherebbe se tutte le regine sono sulla scacchiera e se sono buone l'una con l'altra.
  • state.apply e state.undo modificheranno la scheda per applicare o annullare un posizionamento.

Il primo codice ricorsivo che ho scritto (nel 1984 usando Pascal) per un incarico è stato un algoritmo di risoluzione del labirinto.
Gerry,

Conoscere alcuni semplici compiti in cui posso effettivamente scrivere codice per avere la sensazione reale di queste cose.
nikhil,

@nikhil: stai chiedendo se ci sono dei semplici problemi? È meglio scrivere un pseudo codice per dimostrare il routing generico del backtracking. Ne proverò uno più tardi in una risposta.
Codismo,

Sì, esattamente, sarà di grande aiuto.
nikhil,

Grazie mille, ho letto alcune cose ultimamente. Lentamente ma costantemente la mia comprensione sta migliorando.
nikhil,

5

Hai visto un programma per camminare su un albero binario, giusto? Sembra così:

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)
  }
}

Ecco il tuo backtracking.

In realtà non hai bisogno di un albero fisico. Tutto ciò di cui hai bisogno è un modo per fare una mossa e successivamente annullarla, o dire se hai vinto o dire se non puoi andare oltre.


1
non riesci a restituire un valore bool / int per verificare se la soluzione è stata trovata nella sottostruttura? else{return walk(p->left)||walk(p->right));}non c'è bisogno di lanciare per il risultato atteso
maniaco del cricchetto,

@Ratchet: assolutamente. Questo è anche un modo perfettamente buono per farlo. (Stavo solo cercando di disordinare l'esempio. In realtà lo farei a modo tuo.)
Mike Dunlavey,

Tuttavia, il taglio di @MikeDunlavey è un po 'importante nella pratica.
jupp0r
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.