Jezdcova procházka (Knight's tour) je šachová úloha, jejímž cílem je pomocí šachové figury jezdce navštívit všechna pole šachovnice právě jednou. Tento hlavolam je znám již od středověku – byl popsán kolem roku 840 arabským učencem Al-Ádlím v díle Kitáb aš-Šatrandž (Kniha o šachu).
Jezdcova procházka má překvapivě obrovské množství řešení, pro běžnou šachovnici 8x8 polí existuje 33 439 123 484 294 neorientovaných cest, kterými se může kůň vydat.
Strojové řešení problému
Nejjednodušším způsobem řešením problému je backtracking. Naivní algoritmus má ovšem tendenci být velmi pomalý, protože se dostává snadno do do slepých uliček, v nichž musí přehodnotit velké množství rozhodnutí. Jeho optimalizací je Warnsdorffův algoritmus, ve kterém se kůň vždy vydá přednostně na to pole, ze kterého může pokračovat nejméně způsoby. V této heuristice jsou tedy přednostně obsazována špatně dostupá pole, zatímco ta snadno dostupná jsou odložena na později.
Počet řešení problému
| Velikost šachovnice | 3x1 | 3x2 | 3x3 | 3x4 | 3x5 | 3x6 | 3x7 | 3x8 | 3x9 | 3x10 | 3x11 | 3x12 | 3x13 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Počet neorientovaných řešení | 0 | 0 | 0 | 16 | 0 | 0 | 104 | 792 | 1 120 | 6 096 | 21 344 | 114 496 | 257 728 |
Kód
/**
* Resicka jezdcovy prochazky backtrackingem
* @author Pavel Micka
*/
public class KnightsTour {
/**
* Priznak nenavstivenosti policka
*/
private static int NOT_VISITED = -1;
/**
* Velikost sachovnice na ose x
*/
private int xSize;
/**
* Velikost sachovnice na ose y
*/
private int ySize;
/**
* Pocet reseni
*/
private int solutionsCount;
/**
* Pole pro reseni
* 0 -> pocatecni pozice kone
* 1 -> prvni tah
* 2 -> druhy tah
* .
* .
* .
* n -> n-ty tah
*/
private int[][] solutionBoard;
public static void main(String[] args) {
for (int i = 1; i < 20; i++) {
KnightsTour kt = new KnightsTour(3, i);
kt.solve();
System.out.println("<td>"+kt.solutionsCount+"</td>");
}
}
/**
* Konstruktor resitele jezdcovy prochazky
* @param xSize velikost sachovnice na ose x
* @param ySize velikost sachovnice na ose y
*/
public KnightsTour(int xSize, int ySize) {
solutionsCount = 0;
this.xSize = xSize;
this.ySize = ySize;
solutionBoard = new int[ySize][xSize];
for (int i = 0; i < ySize; i++) {
for (int j = 0; j < xSize; j++) {
solutionBoard[i][j] = NOT_VISITED;
}
}
}
/**
* Resi jezdcovu prochazku
*/
public void solve() {
for (int i = 0; i < ySize; i++) {
for (int j = 0; j < xSize; j++) {
takeTurn(j, i, 0);
solutionBoard[i][j] = NOT_VISITED; //reset pole
}
}
}
/**
* Vrati policka, na ktera muze kun skocit
* @param x souradnice kone x
* @param y souradnice kone y
* @return souradnice, na ktere muze kun skocit
*/
private List<Coords> getFields(int x, int y) {
List<Coords> l = new ArrayList<Coords>();
if (x + 2 < xSize && y - 1 >= 0)
l.add(new Coords(x + 2, y - 1)); //doprava nahoru
if (x + 1 < xSize && y - 2 >= 0)
l.add(new Coords(x + 1, y - 2)); //nahoru doprava
if (x - 1 >= 0 && y - 2 >= 0)
l.add(new Coords(x - 1, y - 2)); //nahoru doleva
if (x - 2 >= 0 && y - 1 >= 0)
l.add(new Coords(x - 2, y - 1)); //doleva nahoru
if (x - 2 >= 0 && y + 1 < ySize)
l.add(new Coords(x - 2, y + 1)); //doleva dolu
if (x - 1 >= 0 && y + 2 < ySize)
l.add(new Coords(x - 1, y + 2)); //dolu doleva
if (x + 1 < xSize && y + 2 < ySize)
l.add(new Coords(x + 1, y + 2)); //dolu doprava
if (x + 2 < xSize && y + 1 < ySize)
l.add(new Coords(x + 2, y + 1)); //doprava dolu
return l;
}
/**
* Provede tah konem
* @param x cilova souradnice x
* @param y cilova souradnice y
* @param turnNr cislo tahu
*/
private void takeTurn(int x, int y, int turnNr) {
solutionBoard[y][x] = turnNr;
if (turnNr == (xSize * ySize) - 1) {
solutionsCount++;
printSolution();
return;
} else {
for (Coords c : getFields(x, y)) {
if (solutionBoard[c.getY()][c.getX()] == NOT_VISITED) {
takeTurn(c.getX(), c.getY(), turnNr + 1);
solutionBoard[c.getY()][c.getX()] = NOT_VISITED; //reset policka
}
}
}
}
/**
* Vypise reseni
*/
private void printSolution() {
System.out.println("Reseni #" + solutionsCount);
for (int i = 0; i < solutionBoard.length; i++) {
for (int j = 0; j < solutionBoard[i].length; j++) {
System.out.print(solutionBoard[i][j] + " ");
}
System.out.println("");
}
System.out.println("");
}
/**
* @return the solutionsCount
*/
public int getSolutionsCount() {
return solutionsCount;
}
/**
* Reprezentuje souradnici
*/
private class Coords {
private int x;
private int y;
public Coords(int x, int y) {
this.x = x;
this.y = y;
}
/**
* @return the x
*/
public int getX() {
return x;
}
/**
* @param x the x to set
*/
public void setX(int x) {
this.x = x;
}
/**
* @return the y
*/
public int getY() {
return y;
}
/**
* @param y the y to set
*/
public void setY(int y) {
this.y = y;
}
}
}
#include <iostream>
#include <vector>
using namespace std;
/**
* Resicka jezdcovy prochazky backtrackingem
* @author Pavel Micka
*/
class KnightsTour {
private:
/**
* Priznak nenavstivenosti policka
*/
static const int NOT_VISITED = -1;
/**
* Velikost sachovnice na ose x
*/
int xSize;
/**
* Velikost sachovnice na ose y
*/
int ySize;
/**
* Pocet reseni
*/
int solutionsCount;
/**
* Pole pro reseni
* 0 -> pocatecni pozice kone
* 1 -> prvni tah
* 2 -> druhy tah
* .
* .
* .
* n -> n-ty tah
*/
int ** solutionBoard;
/**
* Reprezentuje souradnici
*/
class Coords {
private:
int x;
int y;
public:
Coords(int x, int y) {
this->x = x;
this->y = y;
}
/**
* @return the x
*/
int getX() {
return x;
}
/**
* @param x the x to set
*/
void setX(int x) {
this->x = x;
}
/**
* @return the y
*/
int getY() {
return y;
}
/**
* @param y the y to set
*/
void setY(int y) {
this->y = y;
}
};
/**
* Vrati policka, na ktera muze kun skocit
* @param x souradnice kone x
* @param y souradnice kone y
* @param v reference na vector, do ktereho budou ulozeny souradnice
*/
void getFields(int x, int y, vector<Coords> &v) {
if (x + 2 < xSize && y - 1 >= 0)
v.push_back(Coords(x + 2, y - 1)); //doprava nahoru
if (x + 1 < xSize && y - 2 >= 0)
v.push_back(Coords(x + 1, y - 2)); //nahoru doprava
if (x - 1 >= 0 && y - 2 >= 0)
v.push_back(Coords(x - 1, y - 2)); //nahoru doleva
if (x - 2 >= 0 && y - 1 >= 0)
v.push_back(Coords(x - 2, y - 1)); //doleva nahoru
if (x - 2 >= 0 && y + 1 < ySize)
v.push_back(Coords(x - 2, y + 1)); //doleva dolu
if (x - 1 >= 0 && y + 2 < ySize)
v.push_back(Coords(x - 1, y + 2)); //dolu doleva
if (x + 1 < xSize && y + 2 < ySize)
v.push_back(Coords(x + 1, y + 2)); //dolu doprava
if (x + 2 < xSize && y + 1 < ySize)
v.push_back(Coords(x + 2, y + 1)); //doprava dolu
}
/**
* Provede tah konem
* @param x cilova souradnice x
* @param y cilova souradnice y
* @param turnNr cislo tahu
*/
void takeTurn(int x, int y, int turnNr) {
solutionBoard[y][x] = turnNr;
if (turnNr == (xSize * ySize) - 1) {
solutionsCount++;
printSolution();
return;
} else {
vector<Coords> v;
getFields(x, y, v);
for(unsigned i = 0; i < v.size(); i++){
if (solutionBoard[v.at(i).getY()][v.at(i).getX()] == NOT_VISITED) {
takeTurn(v.at(i).getX(), v.at(i).getY(), turnNr + 1);
solutionBoard[v.at(i).getY()][v.at(i).getX()] = NOT_VISITED; //reset policka
}
}
}
}
/**
* Vypise reseni
*/
void printSolution() {
cout << "Reseni #" << solutionsCount << "\\n" ;
for (int i = 0; i < ySize; i++) {
for (int j = 0; j < xSize; j++) {
cout << solutionBoard[i][j] << " ";
}
cout << "\\n";
}
cout << "\\n";
}
public:
/**
* Konstruktor resitele jezdcovy prochazky
* @param xSize velikost sachovnice na ose y
* @param ySize velikost sachovnice na ose y
*/
KnightsTour(int xSize, int ySize) {
solutionsCount = 0;
this->xSize = xSize;
this->ySize = ySize;
solutionBoard = new int*[ySize];
for(int i = 0; i < ySize; i++){
solutionBoard[i] = new int[xSize];
for (int j = 0; j < xSize; j++) {
solutionBoard[i][j] = NOT_VISITED;
}
}
}
~KnightsTour(){
for(int i = 0; i < ySize; i++) delete[] solutionBoard[i];
delete[] solutionBoard;
}
/**
* Resi jezdcovu prochazku
*/
void solve() {
for (int i = 0; i < ySize; i++) {
for (int j = 0; j < xSize; j++) {
takeTurn(j, i, 0);
solutionBoard[i][j] = NOT_VISITED; //reset pole
}
}
}
/**
* @return the solutionsCount
*/
int getSolutionsCount() {
return solutionsCount;
}
};
int main(int argc, char* argv[]) {
KnightsTour t(3, 14);
t.solve();
system("pause");
return 0;
}
Literatura
- Knight's Tour -- from Wolfram MathWorld [online]. 1999-2009 [cit. 2009-09-14]. Dostupný z WWW: <http://mathworld.wolfram.com/KnightsTour.html>.
- LÖBBING , Martin, WEGENER, Ingo. The Number of Knight's Tours Equals 33,439,123,484,294 - Counting with Binary Decision Diagrams. [s.l.] : [s.n.], 1996. 14 s. Dostupný z WWW: <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.32.8394>.