Skip to content

Commit 6e62a8e

Browse files
committed
Add Wansdorff's algorithm implementation and benchmarks
1 parent c3b0cae commit 6e62a8e

1 file changed

Lines changed: 151 additions & 49 deletions

File tree

source_code/knights_tour.ipynb

Lines changed: 151 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
},
1111
{
1212
"cell_type": "code",
13-
"execution_count": 1,
13+
"execution_count": 3,
1414
"id": "d51322fb-c8f6-4d9e-a378-1b3653b86ae4",
1515
"metadata": {},
1616
"outputs": [],
@@ -294,7 +294,7 @@
294294
},
295295
{
296296
"cell_type": "code",
297-
"execution_count": 3,
297+
"execution_count": 4,
298298
"id": "31e73e9c-6f07-4033-a1c8-919db0086eaa",
299299
"metadata": {},
300300
"outputs": [],
@@ -503,7 +503,7 @@
503503
},
504504
{
505505
"cell_type": "code",
506-
"execution_count": 11,
506+
"execution_count": null,
507507
"id": "0ccb1546-3cd9-4ef5-9ad7-f4b488508462",
508508
"metadata": {},
509509
"outputs": [],
@@ -532,7 +532,7 @@
532532
"outputs": [],
533533
"source": [
534534
"def find_tour(n):\n",
535-
" '''find a knight's tour on an n-by-n board\n",
535+
" '''find a knight's tour on an n-by-n board using backtracking\n",
536536
" \n",
537537
" Parameters\n",
538538
" ----------\n",
@@ -888,90 +888,192 @@
888888
},
889889
{
890890
"cell_type": "code",
891-
"execution_count": 4,
892-
"id": "43a3ac96-8353-4828-a0fb-ed5f10421136",
891+
"execution_count": 88,
892+
"id": "c7e766e4-ab0d-4bf0-81f9-4bdc5ec1b15e",
893893
"metadata": {},
894894
"outputs": [],
895895
"source": [
896-
"def compute_initial_accessibility(n):\n",
897-
" return [len(moves) for moves in compute_all_moves(n)]"
896+
"def find_wansdorff_tour(n):\n",
897+
" '''find a knight's tour on an n-by-n board using Wansdorff's algorithm\n",
898+
" \n",
899+
" Parameters\n",
900+
" ----------\n",
901+
" n: int\n",
902+
" size of the n-by-n board\n",
903+
" \n",
904+
" Returns\n",
905+
" -------\n",
906+
" list[int]\n",
907+
" the sequence of positions on the board that form a valid knight's tour,\n",
908+
" or an empty list if no tour can be found\n",
909+
" '''\n",
910+
" def update_moves(all_moves, pos):\n",
911+
" for move in all_moves[pos]:\n",
912+
" try:\n",
913+
" all_moves[move].remove(pos)\n",
914+
" except ValueError:\n",
915+
" pass\n",
916+
" all_moves = compute_all_moves(n)\n",
917+
" tour = [0]\n",
918+
" update_moves(all_moves, tour[-1])\n",
919+
" for move_nr in range(2, n**2 + 1):\n",
920+
" min_pos, min_accessibility = None, n**2\n",
921+
" for move in all_moves[tour[-1]]:\n",
922+
" accessibility = len(all_moves[move])\n",
923+
" if move not in tour and 0 <= accessibility < min_accessibility:\n",
924+
" min_accessibility = accessibility\n",
925+
" min_pos = move\n",
926+
" if min_pos is None:\n",
927+
" return []\n",
928+
" tour.append(min_pos)\n",
929+
" update_moves(all_moves, tour[-1])\n",
930+
" return tour "
898931
]
899932
},
900933
{
901934
"cell_type": "code",
902-
"execution_count": 11,
903-
"id": "57130a92-ba10-4c43-a7da-b55f32c36d49",
935+
"execution_count": 90,
936+
"id": "1cd3254d-c09f-4c57-909f-08fd3f4e9bf5",
904937
"metadata": {},
905-
"outputs": [],
938+
"outputs": [
939+
{
940+
"name": "stdout",
941+
"output_type": "stream",
942+
"text": [
943+
" 1 14 9 20 3\n",
944+
" 24 19 2 15 10\n",
945+
" 13 8 25 4 21\n",
946+
" 18 23 6 11 16\n",
947+
" 7 12 17 22 5\n"
948+
]
949+
}
950+
],
906951
"source": [
907-
"def print_accessibility(accessibility):\n",
908-
" n = math.isqrt(len(accessibility))\n",
909-
" for i in range(n):\n",
910-
" print(' '.join(map(lambda x: f'{x:4d}', accessibility[i*n:(i + 1)*n])))"
952+
"print_tour(find_wansdorff_tour(5))"
953+
]
954+
},
955+
{
956+
"cell_type": "markdown",
957+
"id": "44cdf539-c4c7-4f3c-937b-37a5f4b69ff7",
958+
"metadata": {},
959+
"source": [
960+
"## Performance"
911961
]
912962
},
913963
{
914964
"cell_type": "code",
915-
"execution_count": 12,
916-
"id": "5e4758f3-fa22-4566-bf89-8e90dd563fa7",
965+
"execution_count": 91,
966+
"id": "56eaf07d-91e9-4459-b7a9-8c003402e894",
917967
"metadata": {},
918968
"outputs": [
919969
{
920970
"name": "stdout",
921971
"output_type": "stream",
922972
"text": [
923-
" 2 3 4 3 2\n",
924-
" 3 4 6 4 3\n",
925-
" 4 6 8 6 4\n",
926-
" 3 4 6 4 3\n",
927-
" 2 3 4 3 2\n"
973+
"27.6 µs ± 378 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n"
928974
]
929975
}
930976
],
931977
"source": [
932-
"print_accessibility(compute_initial_accessibility(5))"
978+
"%timeit find_wansdorff_tour(5)"
933979
]
934980
},
935981
{
936982
"cell_type": "code",
937-
"execution_count": 23,
938-
"id": "fabc1605-b986-4d77-9756-5744f122ba30",
983+
"execution_count": 92,
984+
"id": "629fd883-54c4-4268-93be-088917c5979f",
939985
"metadata": {},
940-
"outputs": [],
986+
"outputs": [
987+
{
988+
"name": "stdout",
989+
"output_type": "stream",
990+
"text": [
991+
"43.8 µs ± 270 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n"
992+
]
993+
}
994+
],
941995
"source": [
942-
"def wansdorff(n):\n",
943-
" all_moves = compute_all_moves(n)\n",
944-
" accessibility =[len(moves) for moves in compute_all_moves(n)]\n",
945-
" board = [-1]*n\n",
946-
" pos = 0\n",
947-
" board[0] = 1\n",
948-
" for move_nr in range(2, n**2 + 1):\n",
949-
" pos = min(all_moves[pos], accessibility[k] for k in all_moves[pos] if k not in board)\n",
950-
" board[pos] = move_nr\n",
951-
" return board "
996+
"%timeit find_wansdorff_tour(6)"
952997
]
953998
},
954999
{
9551000
"cell_type": "code",
956-
"execution_count": 24,
957-
"id": "1cd3254d-c09f-4c57-909f-08fd3f4e9bf5",
1001+
"execution_count": 93,
1002+
"id": "ab4f6205-23b7-4962-b277-4efb2a47818f",
1003+
"metadata": {},
1004+
"outputs": [
1005+
{
1006+
"name": "stdout",
1007+
"output_type": "stream",
1008+
"text": [
1009+
"66.2 µs ± 1.58 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n"
1010+
]
1011+
}
1012+
],
1013+
"source": [
1014+
"%timeit find_wansdorff_tour(7)"
1015+
]
1016+
},
1017+
{
1018+
"cell_type": "code",
1019+
"execution_count": 94,
1020+
"id": "b29816be-4099-4a67-b8da-8503878ddd77",
1021+
"metadata": {},
1022+
"outputs": [
1023+
{
1024+
"name": "stdout",
1025+
"output_type": "stream",
1026+
"text": [
1027+
"92.4 µs ± 881 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n"
1028+
]
1029+
}
1030+
],
1031+
"source": [
1032+
"%timeit find_wansdorff_tour(8)"
1033+
]
1034+
},
1035+
{
1036+
"cell_type": "code",
1037+
"execution_count": 96,
1038+
"id": "51b9047e-6986-47e7-9530-b6555c921a4c",
9581039
"metadata": {},
9591040
"outputs": [
9601041
{
961-
"ename": "IndexError",
962-
"evalue": "list assignment index out of range",
963-
"output_type": "error",
964-
"traceback": [
965-
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
966-
"\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)",
967-
"Cell \u001b[0;32mIn[24], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m print_accessibility(wansdorff(\u001b[38;5;241m5\u001b[39m))\n",
968-
"Cell \u001b[0;32mIn[23], line 9\u001b[0m, in \u001b[0;36mwansdorff\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m move_nr \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(\u001b[38;5;241m2\u001b[39m, n\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m2\u001b[39m \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m):\n\u001b[1;32m 8\u001b[0m pos \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mmin\u001b[39m(accessibility[k] \u001b[38;5;28;01mfor\u001b[39;00m k \u001b[38;5;129;01min\u001b[39;00m all_moves[pos] \u001b[38;5;28;01mif\u001b[39;00m k \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m board)\n\u001b[0;32m----> 9\u001b[0m board[pos] \u001b[38;5;241m=\u001b[39m move_nr\n\u001b[1;32m 10\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m board\n",
969-
"\u001b[0;31mIndexError\u001b[0m: list assignment index out of range"
1042+
"name": "stdout",
1043+
"output_type": "stream",
1044+
"text": [
1045+
"63.7 ms ± 338 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
9701046
]
9711047
}
9721048
],
9731049
"source": [
974-
"print_accessibility(wansdorff(5))"
1050+
"%timeit find_wansdorff_tour(50)"
1051+
]
1052+
},
1053+
{
1054+
"cell_type": "code",
1055+
"execution_count": 95,
1056+
"id": "e84c70dc-36aa-4103-ae35-f643bf67d4c2",
1057+
"metadata": {},
1058+
"outputs": [
1059+
{
1060+
"name": "stdout",
1061+
"output_type": "stream",
1062+
"text": [
1063+
"1.29 s ± 27.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
1064+
]
1065+
}
1066+
],
1067+
"source": [
1068+
"%timeit find_wansdorff_tour(100)"
1069+
]
1070+
},
1071+
{
1072+
"cell_type": "markdown",
1073+
"id": "f84d6c6e-5cd4-49ce-b8be-903b114366fc",
1074+
"metadata": {},
1075+
"source": [
1076+
"The runtime is very low, and increases slowly with the size of the board, $O(n)$."
9751077
]
9761078
},
9771079
{
@@ -1007,7 +1109,7 @@
10071109
"name": "python",
10081110
"nbconvert_exporter": "python",
10091111
"pygments_lexer": "ipython3",
1010-
"version": "3.11.2"
1112+
"version": "3.11.3"
10111113
}
10121114
},
10131115
"nbformat": 4,

0 commit comments

Comments
 (0)