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
001.
/**
002.
* Resicka jezdcovy prochazky backtrackingem
003.
* @author Pavel Micka
004.
*/
005.
public
class
KnightsTour {
006.
007.
/**
008.
* Priznak nenavstivenosti policka
009.
*/
010.
private
static
int
NOT_VISITED = -
1
;
011.
/**
012.
* Velikost sachovnice na ose x
013.
*/
014.
private
int
xSize;
015.
/**
016.
* Velikost sachovnice na ose y
017.
*/
018.
private
int
ySize;
019.
/**
020.
* Pocet reseni
021.
*/
022.
private
int
solutionsCount;
023.
/**
024.
* Pole pro reseni
025.
* 0 -> pocatecni pozice kone
026.
* 1 -> prvni tah
027.
* 2 -> druhy tah
028.
* .
029.
* .
030.
* .
031.
* n -> n-ty tah
032.
*/
033.
private
int
[][] solutionBoard;
034.
035.
public
static
void
main(String[] args) {
036.
for
(
int
i =
1
; i <
20
; i++) {
037.
KnightsTour kt =
new
KnightsTour(
3
, i);
038.
kt.solve();
039.
System.out.println(
"<td>"
+kt.solutionsCount+
"</td>"
);
040.
}
041.
}
042.
043.
/**
044.
* Konstruktor resitele jezdcovy prochazky
045.
* @param xSize velikost sachovnice na ose x
046.
* @param ySize velikost sachovnice na ose y
047.
*/
048.
public
KnightsTour(
int
xSize,
int
ySize) {
049.
solutionsCount =
0
;
050.
051.
this
.xSize = xSize;
052.
this
.ySize = ySize;
053.
054.
solutionBoard =
new
int
[ySize][xSize];
055.
for
(
int
i =
0
; i < ySize; i++) {
056.
for
(
int
j =
0
; j < xSize; j++) {
057.
solutionBoard[i][j] = NOT_VISITED;
058.
}
059.
}
060.
}
061.
062.
/**
063.
* Resi jezdcovu prochazku
064.
*/
065.
public
void
solve() {
066.
for
(
int
i =
0
; i < ySize; i++) {
067.
for
(
int
j =
0
; j < xSize; j++) {
068.
takeTurn(j, i,
0
);
069.
solutionBoard[i][j] = NOT_VISITED;
//reset pole
070.
}
071.
}
072.
}
073.
074.
/**
075.
* Vrati policka, na ktera muze kun skocit
076.
* @param x souradnice kone x
077.
* @param y souradnice kone y
078.
* @return souradnice, na ktere muze kun skocit
079.
*/
080.
private
List<Coords> getFields(
int
x,
int
y) {
081.
List<Coords> l =
new
ArrayList<Coords>();
082.
if
(x +
2
< xSize && y -
1
>=
0
)
083.
l.add(
new
Coords(x +
2
, y -
1
));
//doprava nahoru
084.
if
(x +
1
< xSize && y -
2
>=
0
)
085.
l.add(
new
Coords(x +
1
, y -
2
));
//nahoru doprava
086.
if
(x -
1
>=
0
&& y -
2
>=
0
)
087.
l.add(
new
Coords(x -
1
, y -
2
));
//nahoru doleva
088.
if
(x -
2
>=
0
&& y -
1
>=
0
)
089.
l.add(
new
Coords(x -
2
, y -
1
));
//doleva nahoru
090.
if
(x -
2
>=
0
&& y +
1
< ySize)
091.
l.add(
new
Coords(x -
2
, y +
1
));
//doleva dolu
092.
if
(x -
1
>=
0
&& y +
2
< ySize)
093.
l.add(
new
Coords(x -
1
, y +
2
));
//dolu doleva
094.
if
(x +
1
< xSize && y +
2
< ySize)
095.
l.add(
new
Coords(x +
1
, y +
2
));
//dolu doprava
096.
if
(x +
2
< xSize && y +
1
< ySize)
097.
l.add(
new
Coords(x +
2
, y +
1
));
//doprava dolu
098.
return
l;
099.
}
100.
101.
/**
102.
* Provede tah konem
103.
* @param x cilova souradnice x
104.
* @param y cilova souradnice y
105.
* @param turnNr cislo tahu
106.
*/
107.
private
void
takeTurn(
int
x,
int
y,
int
turnNr) {
108.
solutionBoard[y][x] = turnNr;
109.
if
(turnNr == (xSize * ySize) -
1
) {
110.
solutionsCount++;
111.
printSolution();
112.
return
;
113.
}
else
{
114.
for
(Coords c : getFields(x, y)) {
115.
if
(solutionBoard[c.getY()][c.getX()] == NOT_VISITED) {
116.
takeTurn(c.getX(), c.getY(), turnNr +
1
);
117.
solutionBoard[c.getY()][c.getX()] = NOT_VISITED;
//reset policka
118.
}
119.
}
120.
}
121.
}
122.
123.
/**
124.
* Vypise reseni
125.
*/
126.
private
void
printSolution() {
127.
System.out.println(
"Reseni #"
+ solutionsCount);
128.
for
(
int
i =
0
; i < solutionBoard.length; i++) {
129.
for
(
int
j =
0
; j < solutionBoard[i].length; j++) {
130.
System.out.print(solutionBoard[i][j] +
" "
);
131.
}
132.
System.out.println(
""
);
133.
}
134.
System.out.println(
""
);
135.
}
136.
137.
/**
138.
* @return the solutionsCount
139.
*/
140.
public
int
getSolutionsCount() {
141.
return
solutionsCount;
142.
}
143.
144.
/**
145.
* Reprezentuje souradnici
146.
*/
147.
private
class
Coords {
148.
private
int
x;
149.
private
int
y;
150.
151.
public
Coords(
int
x,
int
y) {
152.
this
.x = x;
153.
this
.y = y;
154.
}
155.
156.
/**
157.
* @return the x
158.
*/
159.
public
int
getX() {
160.
return
x;
161.
}
162.
163.
/**
164.
* @param x the x to set
165.
*/
166.
public
void
setX(
int
x) {
167.
this
.x = x;
168.
}
169.
170.
/**
171.
* @return the y
172.
*/
173.
public
int
getY() {
174.
return
y;
175.
}
176.
177.
/**
178.
* @param y the y to set
179.
*/
180.
public
void
setY(
int
y) {
181.
this
.y = y;
182.
}
183.
}
184.
}
001.
#include <iostream>
002.
#include <vector>
003.
004.
using
namespace
std;
005.
/**
006.
* Resicka jezdcovy prochazky backtrackingem
007.
* @author Pavel Micka
008.
*/
009.
class
KnightsTour {
010.
private
:
011.
/**
012.
* Priznak nenavstivenosti policka
013.
*/
014.
static
const
int
NOT_VISITED = -1;
015.
/**
016.
* Velikost sachovnice na ose x
017.
*/
018.
int
xSize;
019.
/**
020.
* Velikost sachovnice na ose y
021.
*/
022.
int
ySize;
023.
/**
024.
* Pocet reseni
025.
*/
026.
int
solutionsCount;
027.
/**
028.
* Pole pro reseni
029.
* 0 -> pocatecni pozice kone
030.
* 1 -> prvni tah
031.
* 2 -> druhy tah
032.
* .
033.
* .
034.
* .
035.
* n -> n-ty tah
036.
*/
037.
int
** solutionBoard;
038.
039.
040.
/**
041.
* Reprezentuje souradnici
042.
*/
043.
class
Coords {
044.
private
:
045.
int
x;
046.
int
y;
047.
public
:
048.
Coords(
int
x,
int
y) {
049.
this
->x = x;
050.
this
->y = y;
051.
}
052.
053.
/**
054.
* @return the x
055.
*/
056.
int
getX() {
057.
return
x;
058.
}
059.
060.
/**
061.
* @param x the x to set
062.
*/
063.
void
setX(
int
x) {
064.
this
->x = x;
065.
}
066.
067.
/**
068.
* @return the y
069.
*/
070.
int
getY() {
071.
return
y;
072.
}
073.
074.
/**
075.
* @param y the y to set
076.
*/
077.
void
setY(
int
y) {
078.
this
->y = y;
079.
}
080.
};
081.
082.
/**
083.
* Vrati policka, na ktera muze kun skocit
084.
* @param x souradnice kone x
085.
* @param y souradnice kone y
086.
* @param v reference na vector, do ktereho budou ulozeny souradnice
087.
*/
088.
void
getFields(
int
x,
int
y, vector<Coords> &v) {
089.
if
(x + 2 < xSize && y - 1 >= 0)
090.
v.push_back(Coords(x + 2, y - 1));
//doprava nahoru
091.
if
(x + 1 < xSize && y - 2 >= 0)
092.
v.push_back(Coords(x + 1, y - 2));
//nahoru doprava
093.
if
(x - 1 >= 0 && y - 2 >= 0)
094.
v.push_back(Coords(x - 1, y - 2));
//nahoru doleva
095.
if
(x - 2 >= 0 && y - 1 >= 0)
096.
v.push_back(Coords(x - 2, y - 1));
//doleva nahoru
097.
if
(x - 2 >= 0 && y + 1 < ySize)
098.
v.push_back(Coords(x - 2, y + 1));
//doleva dolu
099.
if
(x - 1 >= 0 && y + 2 < ySize)
100.
v.push_back(Coords(x - 1, y + 2));
//dolu doleva
101.
if
(x + 1 < xSize && y + 2 < ySize)
102.
v.push_back(Coords(x + 1, y + 2));
//dolu doprava
103.
if
(x + 2 < xSize && y + 1 < ySize)
104.
v.push_back(Coords(x + 2, y + 1));
//doprava dolu
105.
}
106.
107.
/**
108.
* Provede tah konem
109.
* @param x cilova souradnice x
110.
* @param y cilova souradnice y
111.
* @param turnNr cislo tahu
112.
*/
113.
void
takeTurn(
int
x,
int
y,
int
turnNr) {
114.
solutionBoard[y][x] = turnNr;
115.
if
(turnNr == (xSize * ySize) - 1) {
116.
solutionsCount++;
117.
printSolution();
118.
return
;
119.
}
else
{
120.
vector<Coords> v;
121.
getFields(x, y, v);
122.
for
(unsigned i = 0; i < v.size(); i++){
123.
if
(solutionBoard[v.at(i).getY()][v.at(i).getX()] == NOT_VISITED) {
124.
takeTurn(v.at(i).getX(), v.at(i).getY(), turnNr + 1);
125.
solutionBoard[v.at(i).getY()][v.at(i).getX()] = NOT_VISITED;
//reset policka
126.
}
127.
}
128.
}
129.
}
130.
131.
/**
132.
* Vypise reseni
133.
*/
134.
void
printSolution() {
135.
cout <<
"Reseni #"
<< solutionsCount <<
"\\n"
;
136.
for
(
int
i = 0; i < ySize; i++) {
137.
for
(
int
j = 0; j < xSize; j++) {
138.
cout << solutionBoard[i][j] <<
" "
;
139.
}
140.
cout <<
"\\n"
;
141.
}
142.
cout <<
"\\n"
;
143.
}
144.
145.
public
:
146.
/**
147.
* Konstruktor resitele jezdcovy prochazky
148.
* @param xSize velikost sachovnice na ose y
149.
* @param ySize velikost sachovnice na ose y
150.
*/
151.
KnightsTour(
int
xSize,
int
ySize) {
152.
solutionsCount = 0;
153.
154.
this
->xSize = xSize;
155.
this
->ySize = ySize;
156.
157.
solutionBoard =
new
int
*[ySize];
158.
for
(
int
i = 0; i < ySize; i++){
159.
solutionBoard[i] =
new
int
[xSize];
160.
for
(
int
j = 0; j < xSize; j++) {
161.
solutionBoard[i][j] = NOT_VISITED;
162.
}
163.
}
164.
}
165.
~KnightsTour(){
166.
for
(
int
i = 0; i < ySize; i++)
delete
[] solutionBoard[i];
167.
delete
[] solutionBoard;
168.
}
169.
170.
/**
171.
* Resi jezdcovu prochazku
172.
*/
173.
void
solve() {
174.
for
(
int
i = 0; i < ySize; i++) {
175.
for
(
int
j = 0; j < xSize; j++) {
176.
takeTurn(j, i, 0);
177.
solutionBoard[i][j] = NOT_VISITED;
//reset pole
178.
}
179.
}
180.
}
181.
182.
/**
183.
* @return the solutionsCount
184.
*/
185.
int
getSolutionsCount() {
186.
return
solutionsCount;
187.
}
188.
};
189.
190.
191.
192.
193.
int
main(
int
argc,
char
* argv[]) {
194.
KnightsTour t(3, 14);
195.
t.solve();
196.
system
(
"pause"
);
197.
return
0;
198.
}
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>.