使用matlab生成数独
-
常见生成数独程序
常见的数独生成方法是首先产生一张完整的9宫数字图,然后随机挖掉一些数字,产生数独。也就是说要产生数独,首先产生答案。
本人在开始写程序前想在csdn中寻找一些经验,奈何多数大神都是用java和c++写的,而且数学思想都是使用回溯法,回溯法的核心思想为:
1·在一格内随机填入1~9内的一个数,并按照数独规则判断此时这个数是否合适。
2·若合适,则继续填写下一格的数字。
3·若1~9都不合适,也返回上一格,修改其中数字,然后再次尝试填入数字。
4·不断循环,直到81格全部填入数字。
按各位大神的文章来说,这种方法的效率感人,可是我对递归的理解不深,java和c++的基础也比较薄弱,完全看不懂大神的程序,也就无法判断效率到底有多感人。
除此以外还有一些其他思路,比如用0-1规划,约束问题,可是这些方法我连数学原理都没看懂,更不用说程序。
那不用回溯法就不能写数独了吗?当然不是的,不然我也不会来写博客。 -
我的数独产生方法
我的数独产生思想为:
1·首先产生1~9的随机数列,填入九宫图第一行。(此时整个图都是空的,所以一定不会有问题)
2·从第二行第一列开始,在每一格填数前,首先取出此格所在行,所在列,所在宫,已经填入的所有数,判断在1~9中还有哪些数可以填,这些数称为备选数。随机从备选数中选择一个填入此格。
3·不断循环,填入其余72个格。
这种方法乍一听好像可行,按这种思想写出以下程序:
clc
clear
A = zeros(9,9);
a = (1:9);
b = randperm(9);
A(1,:) = b;
for i = 2:9
for j = 1:9
x = A(i,:);
y = A(:,j);
if 0<j && j<4
z = A(:,1:3);
else if 3<j && j<7
z = A(:,4:6);
else
z = A(:,7:9);
end
end
if 0<i && i<4
z = z(1:3,:);
else if 3<i && i<7
z = z(4:6,:);
else
z = z(7:9,:);
end
end
X = x(x~=0);
Y = y(y~=0);
Z = z(z~=0);
t = union(X,Y);
t = union(t,Z);
n = setxor(t,a);
L = length(n);
r = rand(1);
h = ceil(r*L);
if h == 0
break
end
A(i,j) = n(h);
end
if A(i,j) == 0
break
end
end
但实际运行以后生成的大部分都是这种矩阵:
A =
4 2 8 5 1 3 9 6 7
7 9 3 2 6 4 8 5 1
1 6 5 9 7 8 4 2 3
6 1 9 8 4 5 7 3 2
8 4 7 1 9 2 5 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
显然第五行中还缺少3,可是在第8列和第9列中都已经填入了3,这时就出现矛盾了,无法继续进行下去。
但是所有数独答案都满足我上述的思想,而且在我多次运行这段程序时确实输出了一个正确的数独矩阵。也就是说只要多次循环这段程序就可以产生数独,只是循环次数不固定。但是对于计算机来说,这种循环还是很快的,所以我改进了程序如下:
clc
clear
c = 1;%生成数独数量
num = zeros(1,c);%生成一个数独的的循环次数
shudu = zeros(9,9,c);
for k = 1:c
num(1,k) = 0;
while(sum(sum(shudu(:,:,k)))~=405)
A = zeros(9,9);
a = (1:9);
b = randperm(9);
A(1,:) = b;
for i = 2:9
for j = 1:9
x = A(i,:);
y = A(:,j);
if 0<j && j<4
z = A(:,1:3);
else if 3<j && j<7
z = A(:,4:6);
else
z = A(:,7:9);
end
end
if 0<i && i<4
z = z(1:3,:);
else if 3<i && i<7
z = z(4:6,:);
else
z = z(7:9,:);
end
end
X = x(x~=0);
Y = y(y~=0);
Z = z(z~=0);
t = union(X,Y);
t = union(t,Z);
n = setxor(t,a);
L = length(n);
r = rand(1);
h = ceil(r*L);
if h == 0
num(1,k) = num(1,k)+1;
break
end
A(i,j) = n(h);
end
if A(i,j) == 0
break
end
end
shudu(:,:,k) = A;
end
shudu(:,:,k)
end
那这种方法的效率怎么样呢?我使用这段程序一次生成过800个数独答案,取得循环次数的平均值是49次,对于电脑来说不到一秒钟,最多的一次循环了一千四百多次,大约是5~6秒的时间,最短的一次只循环了两次,这个时间也不知道和回溯法比起来怎么样。
- 数独生成
随机挖空比较容易,代码如下:
b = 3;%设置难度,一行中随机挖掉几个数
A;%完整的数独九宫格
for i = 1:9
a = randperm(9);
for j = 1:b
A(i,a(j)) = 0;
end
end
-
优缺点分析
产生答案部分最大的优点就是代码长度很短,便于理解,不需要用到递归这样的高级程序手段,我看其他人的代码长度基本都在一百五十行左右。缺点就在于产生一个答案的时间不固定,且差距较大。
主要缺点集中在产生数独部分,这种方法产生的数独只能保证有解,但并不能保证有唯一解。而且数独的难度并不是仅在每行缺少的数字个数,而重点在于每个空格的备选数是否唯一,每行缺少4个数字可能会比每行缺少6个数字更难。 -
一点小期待
这是我第一次在csdn写博客,还有很多不足之处,功能使用也不是很熟练,希望各位能提出意见,无论是在程序方面,还是在博客文案,排版方面,欢迎交流,感激不尽。
我也在尝试写解数独的程序,但是目前还不完善,如果把每行缺少3个数称为3级难度,那我现有的成果是,3级难度成功率为100%,4级难度成功率为98.5%,5级难度成功率为78%,6级难度成功率为17.8%,希望未来我可以将其完善。