Visszalépéses keresés (algoritmus)
A Programozás Wiki wikiből
A visszalépéses keresés (Backtracking) olyan esetekben használható, amikor a keresési tér fastruktúraként képzelhető el, amiben a gyökérből kiindulva egy csúcsot keresünk. Számtalan ilyen probléma található, a labirintusokban történő útkeresés, bizonyos mintaillesztési feladatok, a könyvtárszerkezetben történő keresés, egyes logikai játékok (pl. Rubik kocka) kirakása is vizzavezethető erre a problémára.
Az algoritmus lényege, hogy a kezdőpontból kiindulva megtesz egy utat, és ha valahol az derül ki, hogy már nem juthat el a célig, akkor visszalép egy korábbi döntési ponthoz, és ott más utat választ.
Az alap algoritmus kódja C++ nyelven:
// igazat ad, ha az útvonal nem a célba vezet // ilyenkor step egy rossz lépés sorszáma bool erroneous( const Path & path, int & step ); // módosítja az útvonalat úgy, hogy // a step-edik lépésben (vagy az előtt) más // irányt választ. Hamissal tér vissza, ha ez lehetetlen bool increase( Path & path, int step ); // visszalépéses keresés bool backtrack( Path & path ) { int i; while( erroneous( path, i ) { if ( ! increase( path, i )) return false; } return true; }
Ha az útvonalak valamiképpen sorbarendezhetők, és tudjuk biztosítani, hogy az increase függvény későbbi utat generáljon minden esetben, akkor (véges problématérben) az algoritmus biztosan véget ér.
Ha az erroneous függvény ezen felül mindig a legutolsó rossz döntés sorszámát adja vissza, és az increase nem hagy ki utakat, akkor az algoritmus megtalálja az első jó megoldást.
Ha az út érvényessége és a következő lépés meghatározásához elég a pillanatnyi helyet ismerni, akkor az útvonal ábrázolható veremmel. Bizonyos feladatok könnyebben kezelhetők, ha ilyenkor a programnyelv beépített vermét használjuk, és egy rekurzívan hívott függvénynek paraméterként csak az útvonal utolsó elemét adjuk át.
Pl. Visszalépéses keresés bináris fában:
#include <string> using namespace std; // igazat ad, ha n célpont bool isgoal( const Node & n ); // visszalépéses keresés bináris fában string bintreesearch( Node * n ) { string res=""; if ( !n ) return res; if ( isgoal( *n ) ) return "goal"; res = bintreesearch( n->left ); if ( res != "" ) return res.insert( 0, "left " ); res = bintreesearch( n->right ); if ( res != "" ) return res.insert( 0, "right " ); return ""; }
A rekurzív megoldás átláthatóbbá teheti a kódot, de figyelni kell arra, hogy a programnyelv verme sem végtelen. Ha az utak hossza túlságosan nagy, az ilyenkor a program megszakadásához vezethet.
Ha a probléma nem fastruktúrával, hanem kört is tartalmazó gráffal írható le, akkor az érintett helyek halmazát is tárolni kell keresés közben. Erre szolgálnak a gráfkereső algoritmusok.