在固定大小的矩阵表示中计算数独单元格邻居的最佳方法

Optimal way to compute neighbors for a sudoku cell in a fixed size matrix representation

提问人:Nikolai Savulkin 提问时间:10/12/2023 最后编辑:Nikolai Savulkin 更新时间:10/25/2023 访问量:93

问:

对于一项大学作业,我正在实现一个数独求解器。实现的一部分是方法邻居,它给定一个单元格 (x) 计算它的邻居。也就是说,哪个域依赖于 x 的值的单元格。

当然,对于数独游戏来说,单元格 (x) 邻居列表是与 x 在同一列、与 x 相同行和与 x 相同的 3x3 分区中的所有单元格的并集。虽然前两个子集可以很容易地用循环计算,但我无法想出一种巧妙的方法来收集分区邻居,因为根据单元在分区中的位置,有 9 种不同的非重叠场景。

也许将值存储在集合或动态列表中并在之后对其进行重复数据删除可能是更好的解决方案,但我正在使用 C 编程语言并希望将库的使用保留到 libc。我在其他任何地方也不需要上述数据结构,所以我对实现它们犹豫不决。

下面是我的实现。

/*
 * neighbours
 * Auxiliary function to populate array with neighbour indices for cell (x) specified by index (idx).
 * That is, populate the array with indices of the cells such that constraints on their domain depend on value of x.
 * This function is only declared to increase level of abstraction and simplify coding.
 * It also doesn't generalize for different board sizes as it assumes all cells have 20 neighbours.
 * It is not intended to be used outside the scope of ac3, and therefore is marked auxiliary.
 *
 * @param idx   index of the cell which neighbours are to be determined
 * @param buf   a pointer to integer array with at least 20 elements (20 are populated)
 * @return      1 if successful, 0 otherwise
 */
int neighbours(int idx, int* buf);

int
neighbours(int idx, int* buf) {
    int x, y, partition_x, partition_y, cursor;

    cursor = 4;

    x = idx % BOARD_WIDTH;
    y = idx / BOARD_WIDTH;

    partition_x = x % 3;
    partition_y = y % 3;


    if (partition_x == 0 && partition_y == 0) {
        buf[0] = IDX(x+1, y+1);
        buf[1] = IDX(x+2, y+2);
        buf[2] = IDX(x+2, y+1);
        buf[3] = IDX(x+1, y+2);
    }

    if (partition_x == 0 && partition_y == 1) {
        buf[0] = IDX(x+1, y-1);
        buf[1] = IDX(x+2, y+2);
        buf[2] = IDX(x+2, y-1);
        buf[3] = IDX(x+1, y+2);
    }

    if (partition_x == 0 && partition_y == 2) {
        buf[0] = IDX(x+1, y-1);
        buf[1] = IDX(x+2, y-2);
        buf[2] = IDX(x+2, y-1);
        buf[3] = IDX(x+1, y-2);
    }

    if (partition_x == 1 && partition_y == 0) {
        buf[0] = IDX(x-1, y+1);
        buf[1] = IDX(x+1, y+2);
        buf[2] = IDX(x+1, y+1);
        buf[3] = IDX(x-1, y+2);
    }

    if (partition_x == 1 && partition_y == 1) {
        buf[0] = IDX(x-1, y-1);
        buf[1] = IDX(x+1, y+2);
        buf[2] = IDX(x+1, y-1);
        buf[3] = IDX(x-1, y+2);
    }

    if (partition_x == 1 && partition_y == 2) {
        buf[0] = IDX(x-1, y-1);
        buf[1] = IDX(x+1, y-2);
        buf[2] = IDX(x+1, y-1);
        buf[3] = IDX(x-1, y-2);
    }

    if (partition_x == 2 && partition_y == 0) {
        buf[0] = IDX(x-1, y+1);
        buf[1] = IDX(x-2, y+2);
        buf[2] = IDX(x-2, y+1);
        buf[3] = IDX(x-1, y+2);
    }

    if (partition_x == 2 && partition_y == 1) {
        buf[0] = IDX(x-1, y-1);
        buf[1] = IDX(x-2, y+2);
        buf[2] = IDX(x-2, y-1);
        buf[3] = IDX(x-1, y+2);
    }

    if (partition_x == 2 && partition_y == 2) {
        buf[0] = IDX(x-1, y-1);
        buf[1] = IDX(x-2, y-2);
        buf[2] = IDX(x-2, y-1);
        buf[3] = IDX(x-1, y-2);
    }


    for (int nx = 0; nx < BOARD_WIDTH; nx++) {
        if (nx == x)
            continue;
        buf[cursor] = IDX(nx, y);
        cursor++;
    }

    for (int ny = 0; ny < BOARD_WIDTH; ny++) {
        if (ny == y)
            continue;
        buf[cursor] = IDX(x, ny);
        cursor++;
    }



    return 1;
}

我正在努力干净地实现该方法,

c 数独

评论


答:

2赞 Weather Vane 10/12/2023 #1

包含单元格 (y, x) 的块位于

int basecol = (x / 3) * 3;
int baserow = (y / 3) * 3;

然后,您可以执行一对简单的嵌套循环

for(int j = baserow; j < baserow + 3; j++) {
    for(int i = basecol; i<basecol + 3; i++) {
        if(j == y && i == x)
            printf(" self  ");
        else
            printf("(%d, %d) ", j, i);
    }
    printf("\n");
}

评论

0赞 Nikolai Savulkin 10/14/2023
这将复制单元格 x 的列和行中的值。这正是我努力寻找更通用的实现的原因。
0赞 Weather Vane 10/15/2023
我以为你的问题是很容易计算哪个块。您可以先在受每个单元格影响的 20 个单元格中创建一个查找表。
0赞 SudoKoach 10/22/2023 #2

这是 Java 中的代码,用于创建网格 81 个单元格中每个单元格的 81 个扇区的查找表。扇区包括 1+20 个单元格,这些单元格位于同一行、同一列或单元格的同一块(上面的 1)中。

int[][] sector_table = createSectorTable();

int[][] createSectorTable() {
    int[][] st = new int[81][20];
    for (int sec = 0; sec < 81; sec++) {
        int celrow = sec / 9, celcol = sec % 9;
        int blo = Blo(sec), colb = 3 * (blo % 3), rowb = 3 * (blo / 3);
        int j = 0;
        for (int i = 0; i < 9; i++) {
            if (i / 3 != celcol / 3) {
                st[sec][j] = i + 9 * celrow;
                j++;
            }
            if (i / 3 != celrow / 3) {
                st[sec][j] = celcol + 9 * i;
                j++;
            }
            if ((colb + i % 3) / 3 == celcol / 3 && (rowb + i / 3) / 3 == celrow / 3) {
                if (sec != colb + i % 3 + 9 * (rowb + i / 3)) {
                    st[sec][j] = colb + i % 3 + 9 * (rowb + i / 3);
                    j++;
                }
            }
        }
        System.out.println("sector: " + sec + " " + Arrays.toString(st[sec]) + "  array.length:" + st[sec].length);
    }
    return st;
}

评论

0赞 Nikolai Savulkin 10/23/2023
你能解释一下为什么它以这种方式组织吗?我认为在最坏的情况下,查找表将需要 81x20 个元素,其中每个单元格索引都将存储它的邻居索引。这似乎是通过为每个单元格创建整个字段的位图来操作的,掩盖了它的非邻居。为什么会这样做?