所以我正在尝试编写一个简单的遗传算法来解决数独(不是最有效的方法,我知道,但它只是练习进化算法)。我有一些问题想出一个有效的评估函数来测试拼图是否已经解决以及有多少错误。我的第一直觉是检查矩阵的每一行和列(以八度为单位,与matlab类似)通过对它们进行排序,检查重复项,然后将它们放回原来的方式,这看起来很长纠缠不清。有什么想法吗?
很抱歉,如果之前有人问过这个......
答案 0 :(得分:1)
加速比:
使用按位运算而不是排序。
我用相当快的速度制作了100行数独求解器。对于你需要实现DLX algorhitm的超速,还有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;
}
答案 1 :(得分:1)
检查很简单,您将为行,列创建集合,如果不存在,则为3x3添加数字,如果不存在,则相应地改变您的适应性。
然而,真正的诀窍是“相应地改变你的健康”。有些问题似乎非常适合GA和ES(进化策略),那就是我们寻求宽容的解决方案,数独有一个确切的答案......很棘手。我的第一个裂缝可能是创建具有可变长度染色体的解决方案(它们可以是固定长度但是9x9与空白)。适应度函数应该能够确定解决方案的哪个部分是有保证的,哪个部分不是(有时候你必须在一个非常艰难的数独游戏中在黑暗中猜测,然后在没有成功的情况下回溯),它会为每个可能的分支创建孩子是一个好主意。
这是一个递归解决方案。但是,您可以从电路板上的不同位置开始扫描。重组将结合具有重叠解决方案的未经验证部分的解决方案。
只要在这种高水平的轻松时尚中思考它,我就可以看到它将如何实现这一点!
只有在有多条路径可以采用时,才会应用变异,毕竟突变是一种猜测。
答案 2 :(得分:0)
我会使用网格的数字作为索引,并增加9个元素长度数组的相应元素=&gt; s_array [x] ++其中x
是从网格中获取的数字。
在检查一行结尾时,每个元素在数组中必须为1。如果0出现在数组中的某个位置,则该行是错误的。
然而,这只是一个简单的健全性检查,如果没有问题,按行。
PS:如果是10年前,我会建议一个带位操作的汇编解决方案(第1位,第2位,第3位等,值为1,2或3)并检查结果是否为2 ^ 10-1。答案 3 :(得分:0)
听起来很不错,除了'把它们放回'部分。您可以将拼图中任意行,列或方格中的数字放入列表中,并以任何方式检查双打。如果有双打,则会出错。如果所有数字都是唯一的,则没有。你不需要从拼图中取出实际的数字,所以也没有必要把它们放回去。
此外,如果您正在编写求解器,则不应进行任何无效移动,因此根本不需要此检查。
答案 4 :(得分:0)
当我解决这个问题时,我只计算了每行,列和子网格中的重复数量(实际上我只需要计算列和子网格中的重复项,因为我的进化运算符设计永远不会引入重复成行)。我只是使用HashSet来检测重复项。有更快的方法但这对我来说足够快。
你可以在my Java applet中看到这个可视化(如果它太快,增加种群大小以减慢它)。彩色方块是重复的。黄色正方形与另一个正方形冲突,橙色与另外两个正方形冲突,红色与三个或更多正方形冲突。
答案 5 :(得分:0)
这是我的解决方案。 Sudoku solving solution in C++
答案 6 :(得分:0)
这是我的solution使用集。如果对于一条线,一个块或一列你得到的设定长度(比如说)7,你的健康状态将是9 - 7.
答案 7 :(得分:0)
如果您使用一小组整数进行操作,可以使用存储桶排序在O(n)
中进行排序。
您可以使用tmp
数组在matlab中执行此任务:
function tf = checkSubSet(board,sel) % %给出9x9板和选择(使用逻辑9x9 sel矩阵) %验证board(sel)有9个唯一元素 % %假设: % - 董事会是9x9,数字1,2,...,9 % - sel只有9个“真实”条目:nnz(sel)= 9 % tmp =零(1,9); tmp(board(sel))= 1; %穷人的水桶分拣 tf = all(tmp == 1)&amp;&amp; nnz(sel)== 9&amp;&amp; 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