【发布时间】:2018-10-15 06:46:23
【问题描述】:
我正在使用 Java 中的 Knight Tour 算法实现。在那段时间里,我完全确信在 C 上的实现必须更快。所以在阅读了 GNU C Reference 之后,代码就完成了,逻辑的实现方式与 Java 相同。
当 C 变体需要更多时间来处理 6x6 板时,您能想象我的奇迹吗?
所以我的问题是如何从技术角度优化下面的代码(即没有启发式优化)。
一些性能说明:在我装有 Ubuntu 的 i5 笔记本电脑上,提供的实现需要 4 个多小时才能解决 6x6 板。 Java 程序使用单线程方法可以在大约 3 小时 18 分钟内完成此任务。
一些算法说明:此实现从板上的所有单元格中找到所有可能的游览,而不仅仅是封闭的游览。也没有使用启发式优化,因为它有助于找到更快的第一次旅行,而不是全部。
编辑: 使用此命令编译且未进行任何优化的代码:gcc knight_tour.c -o knight-tour
#include "stdio.h"
#define BOARD_SIZE 5
#define MAX_MOVE_COUNT BOARD_SIZE*BOARD_SIZE
void printBoard(int[][BOARD_SIZE], int);
void clearBoard(int[][BOARD_SIZE], int);
int knight_move(int[][BOARD_SIZE], int, int, int);
int is_valid_position(int, int);
void calc_all_knight_jumps();
static int ALL_KNIGHT_COL_JUMPS[BOARD_SIZE][BOARD_SIZE][9];
static int ALL_KNIGHT_ROW_JUMPS[BOARD_SIZE][BOARD_SIZE][8];
int main() {
int board[BOARD_SIZE][BOARD_SIZE];
clearBoard(board, BOARD_SIZE);
calc_all_knight_jumps();
int result[BOARD_SIZE][BOARD_SIZE];
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
result[i][j] = knight_move(board, i, j, 1);
}
}
printBoard(result, BOARD_SIZE);
return 0;
}
int knight_move(int board[][BOARD_SIZE], int cpos, int rpos, int level) {
if (level == MAX_MOVE_COUNT)
return 1;
board[cpos][rpos] = level;
int solved_count = 0;
int jump_count = ALL_KNIGHT_COL_JUMPS[cpos][rpos][8];
for (int i = 0; i < jump_count; i++) {
int next_cpos = ALL_KNIGHT_COL_JUMPS[cpos][rpos][i];
int next_rpos = ALL_KNIGHT_ROW_JUMPS[cpos][rpos][i];
if (board[next_cpos][next_rpos] == 0) {
solved_count += knight_move(board, next_cpos, next_rpos, level + 1);
}
}
board[cpos][rpos] = 0;
return solved_count;
}
void clearBoard(int board[][BOARD_SIZE], int size) {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
board[i][j] = 0;
}
}
}
void printBoard(int board[][BOARD_SIZE], int size) {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
printf("%8d", board[i][j]);
}
printf("\n");
}
}
int is_valid_position(int cpos, int rpos) {
if (cpos < 0 || cpos >= BOARD_SIZE) return 0;
if (rpos < 0 || rpos >= BOARD_SIZE) return 0;
return 1;
}
void calc_all_knight_jumps() {
int col_jumps[] = { 1, 2, 2, 1, -1, -2, -2, -1};
int row_jumps[] = { 2, 1, -1, -2, -2, -1, 1, 2};
int next_cpos, next_rpos;
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
int jump_count = 0;
for (int k = 0; k < 8; k++) {
next_cpos = i + col_jumps[k];
next_rpos = j + row_jumps[k];
if (is_valid_position(next_cpos, next_rpos) == 1) {
ALL_KNIGHT_COL_JUMPS[i][j][jump_count] = next_cpos;
ALL_KNIGHT_ROW_JUMPS[i][j][jump_count] = next_rpos;
jump_count++;
}
}
ALL_KNIGHT_COL_JUMPS[i][j][8] = jump_count;
}
}
}
【问题讨论】:
-
分析它并寻找瓶颈,让编译器为你优化事情,如果你尝试这样做 - 可能会使你的代码不可读(一个例子是loop unrolling)。
-
可能是启发式优化,但代码可以使用对称性来改进 x8。
for (col=0; col*2 < N) { for (row=col; row*2 < N)因为板子有垂直、水平和对角对称。 -
只需使用 -O2 或 -O3 进行编译,看看区别...
-
注意:
knight_move()在 5x5 中被调用了 10,000,000 次,这才是真正需要注意的代码。其他功能几乎不需要“从技术角度进行优化”。 -
“代码编译没有任何优化”。你刚刚浪费了四个小时。
标签: c algorithm performance