1. 程式人生 > >遞迴暴力破解法解數獨問題

遞迴暴力破解法解數獨問題

C語言原始碼如下,在Linux平臺下用GCC編譯通過並執行。附資料檔案sudoku.dat及計算結果。

8--------
--36-----
-7--9-2--
-5---7---
----457--
---1---3-
--1----68
--85---1-
-9----4--

812753649
943682175
675491283
154237896
369845721
287169534
521974368
438526917
796318452

編譯及執行方法:

gcc sudoku.c -o sudoku

./sudoku < sudoku.dat

#include <stdio.h>
#include <string.h>

#define ROWS    9
#define COLS    9
#define BLKS    9

#define NAN     '-'

typedef enum { false = 0, true = !false } bool;

struct step {
    char num[ROWS][COLS];
    int cell[ROWS][COLS];
};

static int ctz(int x) { return __builtin_ctz(x); }

static int rowcol2blk(int row, int col) { return row / 3 * 3 + col / 3; }
static int blk2row(int blk) { return blk / 3 * 3; }
static int blk2col(int blk) { return blk % 3 * 3; }

bool setnum(struct step *curr, int row, int col, int num);

void init(struct step *curr)
{
    int row, col;

    for (row = 0; row < ROWS; ++row) {
        for (col = 0; col < COLS; ++col) {
            curr->cell[row][col] = 0x3FE;
            curr->num[row][col] = NAN;
        }
    }
}

bool exclude(struct step *curr, int row, int col, int num)
{
    int x = curr->cell[row][col] & ~(1 << num);

    if (0 == x) return false;

    curr->cell[row][col] = x;
    if (NAN != curr->num[row][col] || 0 != (x & (x - 1)))
        return true;

    return setnum(curr, row, col, ctz(x));
}

bool setnum(struct step *curr, int row, int col, int num)
{
    int mask = 1 << num;
    int blk = rowcol2blk(row, col);
    int r, c, r0, c0;

    if (0 == (curr->cell[row][col] & mask))
        return false;

    curr->num[row][col] = '0' + num;
    curr->cell[row][col] = mask;

    for (r = 0; r < ROWS; ++r) {
        if (r == row) continue;
        if (!exclude(curr, r, col, num))
            return false;
    }

    for (c = 0; c < COLS; ++col) {
        if (c == col) continue;
        if (!exclude(curr, row, c, num))
            return false;
    }

    r0 = blk2row(blk);
    c0 = blk2col(blk);
    for (r = r0; r < r0 + 3; ++r) {
        for (c= c0; c < c0 + 3; ++c) {
            if (r == row && c == col) continue;
            if (!exclude(curr, r, c, num))
                return false;
        }
    }

    return true;
}

bool try(struct step *curr)
{
    struct step next;
    int row, col, num;
    int cell;

    for (row = 0; row < ROWS; ++row) {
        for (col = 0; col < COLS; ++col) {
            if (NAN == curr->num[row][col])
                goto outer;
        }
    }

outer:
    if (row > ROWS) return true;

    cell = curr->cell[row][col];
    while (0 != cell) {
        memcpy(&next, curr, sizeof next);

        num = ctz(cell);
        if (setnum(&next, row, col, num) && try(&next)) {
            memcpy(curr, &next, sizeof *curr);
            break;
        }

        cell &= ~(1 << num);
    }

    return (0 != cell);
}

int main()
{
    struct step step;
    int row, col, num;

    init(&step);

    for (row = 0; row < ROWS; ++row) {
        for (col = 0; col < COLS; ++col) {
            if (NAN == (num = getchar())) continue;
            if (!setnum(&step, row, col, num - '0')) {
                puts("no answer\n");
                return 1;
            }
        }
        num = getchar(); /* new line */
    }

    if (!try(&step)) {
        puts("no answer\n");
        return 1;
    }

    for (row = 0; row < ROWS; ++row) {
        for (col = 0; col < COLS; ++col) {
            putchar(step.num[row][col];
        }
        putchar('\n');
    }

    return 0;
}