【问题标题】:Sudoku solver evaluation function数独求解器评价函数
【发布时间】:2010-12-23 00:39:31
【问题描述】:

所以我正在尝试编写一个简单的遗传算法来解决数独问题(我知道这不是最有效的方法,但这只是为了练习进化算法)。我在想出一个有效的评估函数来测试这个谜题是否得到解决以及有多少错误时遇到了一些问题。我的第一直觉是检查矩阵的每一行和每一列(以八度音程进行,类似于 matlab)通过对它们进行排序、检查重复项然后将它们放回原样来检查它们是否具有唯一元素,这似乎很长缠绕。有什么想法吗?

对不起,如果之前有人问过这个问题......

【问题讨论】:

    标签: matlab evaluation sudoku


    【解决方案1】:

    加速:
    使用按位运算而不是排序。

    我在 c 中制作了 100 行数独求解器,速度相当快。对于需要实现 DLX 算法或超高速的情况,在 matlab 交换中也有一些文件。
    http://en.wikipedia.org/wiki/Exact_cover
    http://en.wikipedia.org/wiki/Dancing_Links
    http://en.wikipedia.org/wiki/Knuth's_Algorithm_X

    #include "stdio.h"
    int rec_sudoku(int (&mat)[9][9],int depth)
    {
        int sol[9][9][10]; //for eliminating
        if(depth == 0) return 1;
        for(int i=0;i<9;i++)
        {
            for(int j=0;j<9;j++)
            {
                sol[i][j][9]=9;
                for(int k=0;k<9;k++)
                {
                    if(mat[i][j]) sol[i][j][k]=0;
                    else sol[i][j][k]=1;
                }
            }
        }
        for(int i=0;i<9;i++)
        {
            for(int j=0;j<9;j++)
            {
                if(mat[i][j] == 0) continue;
                for(int k=0;k<9;k++)
                {
                    if(sol[i][k][mat[i][j]-1])
                    {
                        if(--sol[i][k][9]==0) return 0;
                        sol[i][k][mat[i][j]-1]=0;
                    }
                    if(sol[k][j][mat[i][j]-1])
                    {
                        if(--sol[k][j][9]==0) return 0;
                        sol[k][j][mat[i][j]-1]=0;
                    }
                }
                for(int k=(i/3)*3;k<(i/3+1)*3;k++)
                {
                    for(int kk=(j/3)*3;kk<(j/3+1)*3;kk++)
                    {
                        if(sol[k][kk][mat[i][j]-1])
                        {
                            if(--sol[k][kk][9]==0) return 0;
                            sol[k][kk][mat[i][j]-1]=0;
                        }
                    }
                }
            }
        }
        for(int c=1;c<=9;c++)
        {
            for(int i=0;i<9;i++)
            {
                for(int j=0;j<9;j++)
                {
                    if(sol[i][j][9] != c) continue;
                    for(int k=0;k<9;k++)
                    {
                        if(sol[i][j][k] != 1) continue;
                        mat[i][j]=k+1;
                        if(rec_sudoku(mat,depth-1)) return 1;
                        mat[i][j]=0;
                    }
                    return 0;
                }
            }
        }
        return 0;
    }
    int main(void)
    {
        int matrix[9][9] =
        {
            {1,0,0,0,0,7,0,9,0},
            {0,3,0,0,2,0,0,0,8},
            {0,0,9,6,0,0,5,0,0},
            {0,0,5,3,0,0,9,0,0},
            {0,1,0,0,8,0,0,0,2},
            {6,0,0,0,0,4,0,0,0},
            {3,0,0,0,0,0,0,1,0},
            {0,4,0,0,0,0,0,0,7},
            {0,0,7,0,0,0,3,0,0}
        };
        int d=0;
        for(int i=0;i<9;i++) for(int j=0;j<9;j++) if(matrix[i][j] == 0) d++;
        if(rec_sudoku(matrix,d)==0)
        {
            printf("no solution");
            return 0;
        }
        for(int i=0;i<9;i++)
        {
            for(int j=0;j<9;j++)
            {
                printf("%i ",matrix[i][j]);
            }
            printf("\n");
        }
        return 1;
    }
    

    【讨论】:

      【解决方案2】:

      检查很简单,您将为行、列和 3x3 创建集合,如果不存在则添加一个数字,如果不存在则相应地更改您的适应度。

      然而,真正的诀窍是相应地“改变你的健康状况”。有些问题似乎很适合 GA 和 ES(进化策略),那就是我们在容忍度上寻找解决方案,数独有一个准确的答案……很棘手。

      我的第一次破解可能是创建具有可变长度染色体的解决方案(它们可以是固定长度,但 9x9 带有空白)。适应度函数应该能够确定解决方案的哪一部分得到保证,哪一部分没有保证(有时你必须在一个非常困难的数独游戏中在黑暗中猜测,然后如果它不成功则回溯),它会为每个可能的分支创建子代是个好主意。

      这是一个递归解决方案。但是,您可以从板上的不同位置开始扫描。重组将结合具有重叠解决方案的未验证部分的解决方案。

      只要以这种高级随和的方式思考它,我就可以看到实现这将是多么令人费解!

      只有当有多个路径可供选择时才会应用突变,毕竟突变是一种猜测。

      【讨论】:

        【解决方案3】:

        听起来不错,除了“把它们放回去”部分。您可以将拼图中任意行、列或方格中的数字放入列表中,并以您想要的任何方式检查双打。如果有双打,就有错误。如果所有数字都是唯一的,则不是。您无需从拼图中取出实际数字,因此也无需将它们放回原处。

        此外,如果您正在编写求解器,它不应做出任何无效的移动,因此根本不需要此检查。

        【讨论】:

          【解决方案4】:

          我将使用网格的数字作为索引,并增加一个 9 元素长度数组的相应元素 => s_array[x]++ 其中x 是从网格中获取的数字。 在检查一行结束时,数组中的每个元素都必须为 1。如果数组中某处出现 0,则该行是错误的。

          但是,如果没有问题,这只是一个简单的完整性检查,逐行。

          PS:如果是 10 年前,我建议使用位操作(第 1 位、第 2 位、第 3 位等值 1,2 或 3)的组装解决方案并检查结果是否为 2^ 10-1.

          【讨论】:

            【解决方案5】:

            当我解决这个问题时,我只计算了每行、列和子网格中的重复数(实际上我只需要计算列和子网格中的重复数,因为我的进化算子被设计为从不引入重复数成行)。我只是使用 HashSet 来检测重复项。有更快的方法,但这对我来说已经足够快了。

            您可以在my Java applet 中看到这一点(如果太快,请增加人口规模以减慢速度)。彩色方块是重复的。黄色方格与其他方格冲突,橙色方格与其他两个方格冲突,红色方格与三个或更多方格冲突。

            【讨论】:

              【解决方案6】:

              这是我的解决方案。 Sudoku solving solution in C++

              【讨论】:

                【解决方案7】:

                这是我使用 set 的solution。如果对于一条线、一个块或一列,您的设定长度为(比如说)7,那么您的适应度将是 9 - 7。

                【讨论】:

                  【解决方案8】:
                  1. 如果您对一小组整数进行操作,可以在O(n) 中使用桶排序进行排序。

                  2. 你可以在matlab中使用tmp数组来完成这个任务:

                    function tf = checkSubSet(board, sel) % % 给定一个 9x9 板和一个选择(使用逻辑 9x9 sel 矩阵) % 验证 board(sel) 有 9 个独特的元素 % % 假设: % - 棋盘为 9x9,数字为 1,2,...,9 % - sel 只有 9 个“真实”条目:nnz(sel) = 9 % tmp = 零(1,9); tmp(板(sel))=1; % 穷人桶分类 tf = all(tmp == 1) && nnz(sel) == 9 && numel(tmp) == 9; % 检查有效性

                  现在我们可以使用checkSubSet 来验证板子是否正确

                  function isCorrect = checkSudokuBoard( board )
                  %
                  % assuming board is 9x9 matrix with entries 1,2,...,9
                  %
                  
                  isCorrect = true;
                  % check rows and columns
                  for ii = 1:9
                      sel = false( 9 );
                      sel(:,ii) = true;
                      isCorrect = checkSubSet( board, sel );
                      if ~isCorrect
                          return;
                      end
                      sel = false( 9 );
                      sel( ii, : ) = true;
                      isCorrect = checkSubSet( board, sel );
                      if ~isCorrect
                          return;
                      end
                  end
                  % check all 3x3 
                  for ii=1:3:9
                      for jj=1:3:9
                          sel = false( 9 );
                          sel( ii + (0:2) , jj + (0:2) ) = true;
                          isCorrect = checkSubSet( board, sel );
                          if ~isCorrect
                              return;
                          end
                      end
                  end
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2023-03-27
                    • 2013-03-08
                    • 1970-01-01
                    • 2014-06-06
                    • 1970-01-01
                    相关资源
                    最近更新 更多