目录
- 用Python解数独[0]
- 用Python解数独[1]:求每个单元格的行值域
- 用Python解数独[2]:求列值域和九宫格值域
- 用Python解数独[3]:求总值域
- 用Python解数独[4]:缩减值域
- 用Python解数独[5]:检测唯一值缩减值域
- 用Python解数独[6]:递归获得最终答案
- 用Python解数独[7]:递归(完结篇)
[2][1] 解决数独问题基本思路
6、检测唯一值缩减值域
我们执行代码后,并没有得到最终答案,确切的说,离最终答案还远得很...
PS D:python0624-soduku> python zhihutest.py
[[5, 6, 7], [1], [4], [5, 7, 8], [9], [5, 6, 8], [3], [2], [7, 8]]
[[6, 7, 9], [2, 7, 9], [2, 6, 7, 9], [1, 7, 8], [3], [1, 4, 6, 8], [1, 4, 9], [1, 4, 7, 8, 9], [5]]
[[3], [8], [7, 9], [1, 5, 7], [1, 4, 5, 7], [2], [1, 4, 9], [6], [1, 4, 7]]
[[8], [4], [3, 7, 9], [6], [1, 5], [1, 3, 5], [1, 5, 9], [1, 3, 9], [2]]
[[1], [3, 9], [5], [2], [4, 8], [3, 4, 8], [7], [3, 4, 9], [3, 4, 6]]
[[6, 9], [2, 3, 9], [2, 3, 6, 9], [1, 3, 5, 9], [1, 4, 5], [7], [8], [1, 3, 4, 9], [1, 3, 4, 6]]
[[2], [3, 5, 7], [1, 3, 7, 8], [4], [1, 5, 6, 7, 8], [1, 3, 5, 6, 8], [1, 6], [1, 3, 7, 8], [9]]
[[4, 7], [6], [1, 3, 7, 8], [1, 3, 7, 8], [1, 2, 7, 8], [9], [1, 2, 4], [5], [1, 3, 4, 7, 8]]
[[4, 5, 7, 9], [3, 5, 7, 9], [1, 3, 7, 8, 9], [1, 3, 5, 7, 8], [1, 2, 5, 6, 7, 8], [1, 3, 5, 6, 8], [1, 2, 4, 6], [1, 3, 4, 7, 8], [1, 3, 4, 6, 7, 8]]
代码执行完毕,用时0.0秒
经过一番操作,我们实际上只得到了一个解(第1行第7列),那么我们现在需要思考在现有条件下,还能进一步缩减值域,从而获得新的解吗?
答案是有的!请关注第4行和第7列:
- 第4行,我们发现5个未确定的单元格,只有第3个单元格里有值7,那么我们可以确定,第4行第3列的值就是7!!!
- 第7列,我们发现6个未确定的单元格,只有第3个单元格里有值5,那么我们可以确定,第4行第7列的值就是5!!!
下面我们要做的事情就是把这个“发现”转化为代码,我们先把场景限定为在一行中找“唯一值”:
# 寻找一行中唯一的值,若该值仅在行值域列表中出现了一次,则其所在单元格取值为该值
def checkUnique(list):
listb = copy.deepcopy(list)
templist = []
for i in listb:
templist.extend(i)
for i in range(len(list)):
for j in list[i]:
if templist.count(j) == 1:
listb[i] = [j]
list = listb
return list
这段代码的含义就是:
- 先创建一个list的拷贝listb,然后把listb里每一个子列表合并为一个大列表templist
- 然后去看list子列表中的每一个值,在templist中是否唯一,如果唯一就修改listb中的值
- 最终将listb的值赋给list,返回list
很容易,我们可以得到寻找每一行唯一值的方法:
# 寻找每一行的唯一值,更新值域列表
def row_checkUnique(s_row_vrange):
temp = []
for list in s_row_vrange:
temp.append(checkUnique(list))
s_row_vrange = temp
return s_row_vrange
得到了每一行的方法,每一列和每一个九宫格的方法也就出来了
# 检查值域列表每一行、每一列、每一个九宫格的值域,并寻找唯一值,更新值域列表
def soduku_checkUnique(s_row_vrange):
temp = []
temp_b = []
s_row_vrange = row_checkUnique(s_row_vrange)
for i in list(zip(*s_row_vrange)):# 将数独进行行列转换,然后对每一列进行唯一值检测
temp.append(list(i))
temp = row_checkUnique(temp)
for i in list(zip(*temp)):
temp_b.append(list(i))
temp_c = matrix_invert(temp_b)# 将数独进行九宫格转换,然后对每一九宫格进行唯一值检测
temp_c = row_checkUnique(temp_c)
temp_d = matrix_invert(temp_c)
return temp_d
考虑到检测出唯一值后,单元格的值域肯定会发生变化,因此我们需要对原来的值域列表缩减函数进行一次改造,在获得数独总值域后,立即进行唯一值检测,然后再把值域列表转化为新的数独题目,不断进行循环:
# 值域列表缩减函数:将值域列表转化为数独题目,求新的数独题目的值域列表,如此反复
def reduce_totalValueRange(soduku):
for n in range(100):
total_value_range = totalValueRange(soduku)
total_value_range = soduku_checkUnique(total_value_range)
soduku = generator_soduku(total_value_range)
if total_value_range == totalValueRange(generator_soduku(total_value_range)):
break
return total_value_range
我们运行代码,看一下能否得到最终答案:
PS D:python0624-soduku> python zhihutest2.py
[[5], [1], [4], [7, 8], [9], [6], [3], [2], [7, 8]]
[[7], [2], [6], [1, 8], [3], [1, 4, 8], [9], [1, 4, 8], [5]]
[[3], [8], [9], [5], [4, 7], [2], [1, 4], [6], [1, 4, 7]]
[[8], [4], [7], [6], [1], [3], [5], [9], [2]]
[[1], [9], [5], [2], [4, 8], [4, 8], [7], [3], [6]]
[[6], [3], [2], [9], [5], [7], [8], [1, 4], [1, 4]]
[[2], [5, 7], [3], [4], [6, 7, 8], [1, 5, 8], [1, 6], [1, 7, 8], [9]]
[[4], [6], [1, 8], [1, 3, 7, 8], [2, 7, 8], [9], [1, 2], [5], [1, 3, 7, 8]]
[[9], [5, 7], [1, 8], [1, 3, 7, 8], [2, 6, 7, 8], [1, 5, 8], [1, 2, 4, 6], [1, 4, 7, 8], [1, 3, 4, 7, 8]]
代码执行完毕,用时0.02秒
相比于一开始,我们的确有了很大的进步,从1个确认答案,到25个确认答案。但不同于“入门”级别的数独,对于这个“初级”难度的数独,我们依然没有得到最终的答案,那么我们还需要怎么做才能得到最终答案呢?
预知后事如何,且听下回分解。
下一篇链接:用Python解数独[6]:递归获得最终答案