我正在玩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;
}
}
}
答案 0 :(得分:1)
以下是有关您的代码的一些建议以及答案中发布的更新版本:
对标准头文件使用<>
:
#include <stdio.h>
在宏定义中带有括号的环绕表达式:
#define MAX_MOVE_COUNT (BOARD_SIZE * BOARD_SIZE)
在声明不带参数的函数时使用(void)
:
void pre_calc_all_knight_jumps(void);
避免将浮点数和整数计算与隐式转换混合使用。改用它:
int center = (BOARD_SIZE + 1) / 2;
某些对称性未正确反映在result
数组中。您应该将main
循环更改为:
int border = BOARD_SIZE - 1;
int center = (BOARD_SIZE + 1) / 2;
for (int i = 0; i < center; i++) {
for (int j = i; j < center; j++) {
int res = knight_move(board, i, j, 1);
result[i][j] = res;
result[j][i] = res;
result[border - i][j] = res;
result[j][border - i] = res;
result[i][border - j] = res;
result[border - j][i] = res;
result[border - i][border - j] = res;
result[border - j][border - i] = res;
}
}
通过将板子设置为8x8并在游戏区域的大小上使用其他参数,我还获得了一些缓存使用方面的改进。
肯定需要一种更有效的算法来解决较大尺寸的问题。
答案 1 :(得分:0)
考虑到我修改了源代码的所有注释。
printf()
; 最后我预计会有结果 - C代码更快(但有优化选项)
如果有人对C和Java上的骑士游览算法感兴趣,以下是两种实现: - )
GNU C
#include "stdio.h"
#include "time.h"
#define BOARD_SIZE 6
#define MAX_MOVE_COUNT BOARD_SIZE*BOARD_SIZE
int knight_move(int[][BOARD_SIZE], int, int, int);
void pre_calc_all_knight_jumps();
void print_result(int[][BOARD_SIZE]);
static int ALL_KNIGHT_COL_JUMPS[BOARD_SIZE][BOARD_SIZE][9];
static int ALL_KNIGHT_ROW_JUMPS[BOARD_SIZE][BOARD_SIZE][8];
int main() {
// init board
int board[BOARD_SIZE][BOARD_SIZE];
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
board[i][j] = 0;
}
}
pre_calc_all_knight_jumps();
int result[BOARD_SIZE][BOARD_SIZE];
struct timespec s_time, e_time;
clock_gettime(CLOCK_MONOTONIC, &s_time);
int border = BOARD_SIZE - 1;
int center = BOARD_SIZE / 2.0 + 0.5;
for (int i = 0; i < center; i++) {
for (int j = i; j < center; j++) {
int res = knight_move(board, i, j, 1);
result[i][j] = res;
result[border - i][j] = res;
result[i][border - j] = res;
result[border - i][border - j] = res;
if (i != j) result[j][i] = res;
}
}
clock_gettime(CLOCK_MONOTONIC, &e_time);
printf("Duration in seconds: %ld\n", e_time.tv_sec - s_time.tv_sec);
print_result(result);
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 valid_move_count = ALL_KNIGHT_COL_JUMPS[cpos][rpos][8];
for (int i = 0; i < valid_move_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 print_result(int board[][BOARD_SIZE]) {
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
printf("%8d", board[i][j]);
}
printf("\n");
}
}
void pre_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 (next_cpos < 0 || next_cpos >= BOARD_SIZE) continue;
if (next_rpos < 0 || next_rpos >= BOARD_SIZE) continue;
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;
}
}
}
<强>爪哇强>
import java.util.Arrays;
public class KnightTour1 {
private final static int BOARD_SIZE = 6;
private final static int MAX_MOVE_COUNT = BOARD_SIZE * BOARD_SIZE;
private static final int[][][] ALL_KNIGHT_COL_MOVES;
private static final int[][][] ALL_KNIGHT_ROW_MOVES;
static {
final int[] knightColJumps = { 1, 2, 2, 1, -1, -2, -2, -1};
final int[] knightRowJumps = { 2, 1, -1, -2, -2, -1, 1, 2};
ALL_KNIGHT_COL_MOVES = new int[BOARD_SIZE][BOARD_SIZE][];
ALL_KNIGHT_ROW_MOVES = new int[BOARD_SIZE][BOARD_SIZE][];
int[] tmpColMoves = new int[8];
int[] tmpRowMoves = new int[8];
for (int c = 0; c < BOARD_SIZE; c++) {
for (int r = 0; r < BOARD_SIZE; r++) {
int jumpCount = 0;
for (int i = 0; i < 8; i++) {
int nextColPos = c + knightColJumps[i];
int nextRowPos = r + knightRowJumps[i];
if (isValidBoardPos(nextColPos, nextRowPos)) {
tmpColMoves[jumpCount] = nextColPos;
tmpRowMoves[jumpCount] = nextRowPos;
jumpCount++;
}
}
ALL_KNIGHT_COL_MOVES[c][r] = Arrays.copyOf(tmpColMoves, jumpCount);
ALL_KNIGHT_ROW_MOVES[c][r] = Arrays.copyOf(tmpRowMoves, jumpCount);
}
}
}
private static boolean isValidBoardPos(int colPos, int rowPos) {
return colPos > -1 && colPos < BOARD_SIZE && rowPos > -1 && rowPos < BOARD_SIZE;
}
public static void main(String[] args) {
long sTime = System.currentTimeMillis();
int[][] result = findNumberOfTours();
long duration = (System.currentTimeMillis() - sTime) / 1000;
System.out.println("Duration in seconds: " + duration);
printResult(result);
}
private static int knightMove(int[][] board, int colPos, int rowPos, int level) {
if (level == MAX_MOVE_COUNT) return 1;
board[colPos][rowPos] = level;
final int[] validColMoves = ALL_KNIGHT_COL_MOVES[colPos][rowPos];
final int[] validRowMoves = ALL_KNIGHT_ROW_MOVES[colPos][rowPos];
final int validMoveCount = validColMoves.length;
int solvedTourCount = 0;
for (int i = 0; i < validMoveCount; i++) {
final int nextColPos = validColMoves[i];
final int nextRowPos = validRowMoves[i];
if (board[nextColPos][nextRowPos] == 0) {
solvedTourCount += knightMove(board, nextColPos, nextRowPos, level + 1);
}
}
board[colPos][rowPos] = 0;
return solvedTourCount;
}
private static int[][] findNumberOfTours() {
final int[][] result = new int[BOARD_SIZE][BOARD_SIZE];
final int[][] board = new int[BOARD_SIZE][BOARD_SIZE];
final int border = BOARD_SIZE - 1;
final int center = (int)(BOARD_SIZE / 2f + 0.5);
for (int i = 0; i < center; i++) {
for (int j = i; j < center; j++) {
int res = knightMove(board, i, j, 1);
result[i][j] = res;
result[border - i][j] = res;
result[i][border - j] = res;
result[border - i][border - j] = res;
if (i != j) result[j][i] = res;
}
}
return result;
}
private static void printResult(int[][] res) {
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
System.out.print(String.format("%8d", res[i][j]));
}
System.out.println();
}
}
}