Matlab 数独解算器求值函数
所以我正试图写一个简单的遗传算法来解决数独问题(我知道这不是最有效的方法,但它只是为了练习进化算法)。我遇到了一些问题,需要一个有效的评估函数来测试这个难题是否得到解决,以及有多少错误。我的第一反应是检查矩阵的每一行和每一列(以倍频程进行,类似于matlab)是否有唯一的元素,方法是对它们进行排序,检查重复的元素,然后将它们放回原来的状态,这似乎很冗长。有什么想法吗Matlab 数独解算器求值函数,matlab,evaluation,sudoku,Matlab,Evaluation,Sudoku,所以我正试图写一个简单的遗传算法来解决数独问题(我知道这不是最有效的方法,但它只是为了练习进化算法)。我遇到了一些问题,需要一个有效的评估函数来测试这个难题是否得到解决,以及有多少错误。我的第一反应是检查矩阵的每一行和每一列(以倍频程进行,类似于matlab)是否有唯一的元素,方法是对它们进行排序,检查重复的元素,然后将它们放回原来的状态,这似乎很冗长。有什么想法吗 很抱歉,如果之前有人问过这个问题…我会使用网格的数字作为索引,并增加9个元素长度数组的相应元素=>s_数组[x]++,其中x是从网
很抱歉,如果之前有人问过这个问题…我会使用网格的数字作为索引,并增加9个元素长度数组的相应元素=>s_数组[x]++,其中
x
是从网格中获取的数字。
在检查一行结束时,数组中的每个元素都必须是1。如果0出现在数组中的某个位置,则该行是错误的
然而,这只是一个简单的健全检查,如果没有问题,逐行检查
PS:如果是10年前,我会建议使用位操作的组装解决方案(1位、2位、3位等,用于值1、2或3),并检查结果是否为2^10-1。听起来不错,除了“放回”部分。你可以把拼图中任何一行、一列或一个正方形的数字放在一个列表中,然后按你想要的方式检查双倍数字。如果有双精度,则存在错误。如果所有数字都是唯一的,那么就没有了。您不需要从拼图中取出实际数字,因此也不需要将它们放回原处 此外,如果您正在编写解算器,它不应该执行任何无效的移动,因此根本不需要进行此检查。加速:
使用按位操作而不是排序 我用c语言制作了100行数独解算器,速度相当快。对于实现DLX algorhitm所需的高速或超高速,在matlab exchange上也有相关文件。
s_算法
#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;
}
#包括“stdio.h”
int rec_数独(int(&mat)[9][9],int深度)
{
int sol[9][9][10];//用于消除
如果(深度==0)返回1;
对于(int i=0;i检查很容易,您将为行、列和3x3创建集合,如果不存在,则添加一个数字,如果不存在,则相应地更改适合度
然而,真正的诀窍是相应地“改变你的适应度”。有些问题似乎非常适合GA和ES(进化策略),也就是说,我们在宽容中寻找解决方案,数独有一个确切的答案……很棘手
我的第一个难题可能是创建具有可变长度染色体的解决方案(它们可以是固定长度的,但9x9带有空白)。适应度函数应该能够确定解决方案的哪一部分有保证,哪一部分没有保证(有时候你必须在一个非常难玩的数独游戏中在黑暗中猜测,如果不成功的话,你必须回溯),为每个可能的分支创建子元素是一个好主意
这是一个递归解决方案。但是,您可以从电路板上的不同位置开始扫描。重组将组合解决方案,这些解决方案组合具有重叠解决方案的未验证部分
只要以这种高层次的轻松方式思考一下,我就能看出这将是多么令人费解
只有当有多条路径可供选择时才会应用变异,毕竟变异是一种猜测。当我解决这个问题时,我只计算了每行、每列和子网格中重复的数量(事实上,我只需要在列和子网格中计算重复项,因为我的进化运算符的设计从未将重复项引入行)。我只是使用哈希集来检测重复项。有更快的方法,但这对我来说足够快了
您可以在中看到这一点(如果速度太快,请增加人口规模以降低速度)。彩色方块是重复的。黄色方块与另一个方块冲突,橙色方块与另外两个方块冲突,红色方块与三个或更多个方块冲突。这是我的解决方案。这是我的使用集。如果一条线、一个块或一列的设置长度(比方说)为7,则适合度为9-7
如果您操作的是一小组整数,则可以使用桶排序在O(n)
中进行排序
您可以使用tmp
数组在matlab中执行此任务:
功能tf=检查子集(电路板、sel)
%
%给定一个9x9板和一个选择(使用逻辑9x9 sel矩阵)
%验证电路板(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