기사의 여행
기사의 여행은 체스판 위의 임의의 위치에서 기사가 출발하여 각 위치를 오직 한 번만 방문하면서 모든 위치를 방문하는 순서를 구하는 문제입니다. 오일러 등의 많은 수학자들이 이 문제를 다루었으며, 다양한 해법과 변형된 문제들이 있습니다. 가장 일반적인 해법은 되추적을 이용한 방법입니다.
19세기의 H. C. Warnsdorff는 기사의 여행 문제를 푸는 실용적인 방법을 제시하였습니다. 기사가 움직이면서 어느 곳으로도 움직일 수 없는 막다른 곳에 다다르지 않게 하는 것이 목적입니다. 막다른 곳에 다다르지 않게 하기 위하여 Warnsdorff가 제시한 규칙은 현재 기사가 한 번에 갈 수 있는 곳 중에서 다음 번 수에 갈 수 있는 곳이 가장 적은 곳으로 간다는 규칙입니다. 이 방법은 휴리스틱한 방법입니다만, 8 x 8의 체스판 공간에서 기사의 여행 문제를 잘 풀어 줍니다. 체스판의 공간이 넓어지면 제대로 풀리지 않는 경우가 생길 수 있습니다.
Warnsdorff의 규칙을 이용하여 기사의 여행 문제를 푸는 간단한 프로그램을 작성해 보았습니다. C언어로 작성되어 있고 시작행과 시작열의 위치를 기본 입력에서 읽어서 기본 출력으로 해를 출력해 줍니다.
1 /*
2 * Knight's Tour.
3 *
4 * Author: Jay
5 * Date: 20th of December, 2006
6 */
7 #include <stdio.h>
8
9 /* definitions */
10 #define ROW_SIZE 8
11 #define COL_SIZE 8
12 #define NUM_WAYS 8
13 typedef int board_t[ROW_SIZE][COL_SIZE];
14 int dr[NUM_WAYS] = {-2, -1, 1, 2, 2, 1, -1, -2};
15 int dc[NUM_WAYS] = {1, 2, 2, 1, -1, -2, -2, -1};
16
17 /**
18 * Set every element to -1
19 */
20 void initialize_board(board_t board)
21 {
22 int i, j;
23 for (i=0; i<ROW_SIZE; i++)
24 for (j=0; j<COL_SIZE; j++)
25 board[i][j] = -1;
26 }
27
28 /**
29 * Print the board out.
30 */
31 void print_board(board_t board)
32 {
33 int i, j;
34 for (i=0; i<ROW_SIZE; i++)
35 {
36 for (j=0; j<COL_SIZE; j++)
37 printf("%d\t", board[i][j]);
38 printf("\n");
39 }
40 }
41
42 /**
43 * Check if (r,c) is inside board.
44 * @return true if (r,c) is inside board, false otherwise.
45 */
46 int is_inside_board(int r, int c)
47 {
48 return r >= 0 && r < ROW_SIZE && c >= 0 && c < COL_SIZE;
49 }
50
51 /**
52 * Check if (r,c) is available in board.
53 * @return true if (r,c) is available, that is has value -1,
54 * false otherwise.
55 */
56 int is_available(board_t board, int r, int c)
57 {
58 return is_inside_board(r, c) && board[r][c] == -1;
59 }
60
61 /**
62 * @return number of next moves of (r,c) in board.
63 */
64 int num_next_moves(board_t board, int r, int c)
65 {
66 int i, result=0;
67 for (i=0; i<NUM_WAYS; i++)
68 if (is_available(board, r+dr[i], c+dc[i]))
69 result++;
70 return result;
71 }
72
73 /**
74 * Get next way id from (r,c) in board.
75 * Next way is the way whose destination has minimal number of next moves.
76 * @return next way id, which is in [0, NUM_WAYS).
77 */
78 int next_way_of(board_t board, int r, int c)
79 {
80 int i, min = NUM_WAYS, result=0;
81 for (i=0; i<NUM_WAYS; i++)
82 if (is_available(board, r+dr[i], c+dc[i])
83 && num_next_moves(board, r+dr[i], c+dc[i]) < min)
84 {
85 min = num_next_moves(board, r+dr[i], c+dc[i]);
86 result = i;
87 }
88 return result;
89 }
90
91 /**
92 * Get r, c from user and solve knight tour problem.
93 * Print result out.
94 * @return 0 for successful moves, 1 otherwise.
95 */
96 int main()
97 {
98 int r, c, move, next_way;
99 board_t board;
100
101 initialize_board(board);
102 while (1)
103 {
104 printf("Input start position r c: ");
105 scanf("%d %d", &r, &c);
106 fflush(stdin);
107 if (is_inside_board(r, c)) break;
108 printf("Please put them again.\n");
109 }
110 board[r][c] = 0;
111
112 for (move=1; move<ROW_SIZE*COL_SIZE; move++)
113 {
114 if (num_next_moves(board, r, c) == 0)
115 {
116 printf("Failed.\n");
117 print_board(board);
118 return 1;
119 }
120 next_way = next_way_of(board, r, c);
121 r = r + dr[next_way];
122 c = c + dc[next_way];
123 board[r][c] = move;
124 }
125 print_board(board);
126
127 return 0;
128 }
참고할 수 있는 URL:
Warnsdorff's rule - 영문 페이지
댓글 없음:
댓글 쓰기