python 生成等间距一维数组 python 等间隔数组_数独


目录

  1. 用Python解数独[0]
  2. 用Python解数独[1]:求每个单元格的行值域
  3. 用Python解数独[2]:求列值域和九宫格值域
  4. 用Python解数独[3]:求总值域
  5. 用Python解数独[4]:缩减值域
  6. 用Python解数独[5]:检测唯一值缩减值域
  7. 用Python解数独[6]:递归获得最终答案
  8. 用Python解数独[7]:递归(完结篇)

[0] 前言

首先声明阅读本文需要哪些Python基础知识:

for循环
if条件判断
列表(list)的基本方法:list.append(), list.remove(), list.count(), list.extend(), len(list)
函数基本概念

具备这些知识就足够了,我们将使用这些“基础知识”,做一个足够“复杂”的项目。

然后需要解释下为什么要用数独项目帮助新手入门:

个人原因,我自己很喜欢数独这个游戏,以前也花了很多时间去玩;
数独这个游戏足够纯粹,用一个二维数组就能表示出来,不需要其它任何属性;
解决一个数独问题,过程足够复杂,而用到的Python知识又非常基础;

因此我个人认为数独项目非常适合刚刚掌握Python基础知识的新手,大胆上手练习,仔细学习思考,能帮你建立信心并帮助成长。

现在,我们正式开始吧。

[1] 数独问题


python 生成等间距一维数组 python 等间隔数组_python 数组间隔取值_02


数独的规则很简单,在一个9*9的棋盘上,已经出现了若干个数字(1-9),要求根据已有数字遵照规则求出所有空白地方的数字,规则如下:

数字范围是1-9;
每一行数字不能重复;
每一列数字不能重复;
粗实线划分出的9个九宫格,每个九宫格内数字不能重复;

那么我们在解决数独问题时,会遇到如下两种情况:

  1. 可以根据规则,直接得出某个单元格的答案


python 生成等间距一维数组 python 等间隔数组_Python_03

第9行第4列的单元格,因为第4列已经有8个数字,因此可以直接确定该单元格数字为8

python 生成等间距一维数组 python 等间隔数组_数组_04

第6行第1列的单元格,第6行只剩下数字2和9未出现,而第1列已经出现数字9,因此该单元格数字为2

2. 根据规则无法直接得出答案,只能得到取值范围


python 生成等间距一维数组 python 等间隔数组_python 数组间隔取值_05

第1行第1列,根据行判断可能的取值是[2,4,5,6,7],根据列判断可能的取值是[1,2,3,5,6],根据九宫格判断可能的取值是[1,3,4,5,6],综合三个取值,最终取值范围是[5,6]

[2] 数独模型和解决数独方法

[2][0] 用二维数组表示数独

数独可以很直观地用二维数组来表示,所谓二维数组可以简单理解为,数组中的每一个元素依然是一个数组

我们可以用空字符串 '' 来表示空白单元格,有数字的单元格在对应位置填上数字即可,类似于这样


soduku = [[] for i in range(9)]

soduku[0] = ['',8,9,1,'',3,'','','']
soduku[1] = ['',2,7,4,'','',8,'','']
soduku[2] = ['','','',5,2,'','','','']
soduku[3] = ['',7,6,9,'','',5,'',8]
soduku[4] = [8,'',3,6,'',1,2,'','']
soduku[5] = ['',4,5,3,8,7,1,6,'']
soduku[6] = [7,'',8,2,1,4,3,9,5]
soduku[7] = [9,3,1,7,6,5,4,'',2]
soduku[8] = [4,'',2,'','',9,'','',6]


如果看不懂


soduku = [[] for i in range(9)]


其实就是


soduku = []
for i in range(9):
    soduku.append([])


或者是


soduku = [[],[],[],[],[],[],[],[],[]]


[2][1] 解决数独问题基本思路

经过简单的分析,我们其实已经总结出解决数独问题的方法:

  1. 根据单元格所处的行、列、九宫格,分别计算出单元格的行值域、列值域和九宫格值域;(所谓值域,就是指该单元格的取值范围)
  2. 3个值域求交集,得到单元格最终的值域;

那么如何用代码实现以上的基本思路呢,不要着急,我们梳理一下需要做哪些事情:

  1. 求每个单元格的行值域

只有该行的数字会影响到单元格的行值域,因此求数独每个单元格的行值域,可以简化为求每一行每个单元格的行值域,也就是把相同的事情重复做9次就好了。

如何求一行每个单元格的行值域呢,比如第一行是


soduku[0] = ['',8,9,1,'',3,'','','']


直观地去看,其实第一行空白单元格的行值域,就是这一行未出现数字的集合,即[2,4,5,6,7],每个单元格的行值域都是[2,4,5,6,7]。代码实现思路也就很明显了,数字全集是[1,2,3,4,5,6,7,8,9],这一行里出现了哪些数字,我们就从全集里去除这些数字,最后剩下数字组成的数组就是这一行每个单元格的行值域。


row = ['',8,9,1,'',3,'','','']
row_value_range = list(range(1,10)) # range(1,10)并不是列表类型,因此需要用list()方法转化为列表
for i in row:
        if i == '': # 不要忘记数组中还有空单元格
            continue
        else:
            row_value_range.remove(i)


改写为函数


row = ['',8,9,1,'',3,'','','']

def valueRange(row):
    row_value_range = list(range(1,10))
    for i in row:
        if i == '':
            continue
        else:
            row_value_range.remove(i)
    return row_value_range

print(valueRange(row))

PS D:python0624-soduku> python test7.py
[2, 4, 5, 6, 7]


但是有一个问题,其实我们希望得到的输出是


[[2, 4, 5, 6, 7],8,9,1,[2, 4, 5, 6, 7],3,[2, 4, 5, 6, 7],[2, 4, 5, 6, 7],[2, 4, 5, 6, 7]]


为了保持元素的统一,即数组内元素都是数组(而不是数组和数字掺杂),应该得到如下输出


[[2, 4, 5, 6, 7],[8],[9],[1],[2, 4, 5, 6, 7],[3],[2, 4, 5, 6, 7],[2, 4, 5, 6, 7],[2, 4, 5, 6, 7]]


那么我们的行值域函数还需要继续改进


row = ['',8,9,1,'',3,'','','']

def valueRange(row):
    temp = copy.deepcopy(row)
    row_value_range = list(range(1,10))
    for i in row:
        if i == '':
            continue
        else:
            row_value_range.remove(i)
    for j in temp:
        if j == '':
            j = row_value_range
        else:
            j = [j]
    return temp


看起来代码逻辑很合理,然后执行之后发现


PS D:python0624-soduku> python test5.py
['', 8, 9, 1, '', 3, '', '', '']


哦?为什么没有任何变化???

预知后事如何,且听下回分解

[1]自学能力强的同学可以直接到github看完整代码

[2]本文中数独题目来自8月2日入门级别题目(网站链接见最下方),网站很好用,大家可以去玩玩


下一篇链接:用Python解数独[1]:求每个单元格的行值域