上文中给出了数独的生成方法,那么如何出题呢?最常见的是在随机生成数独之后进行挖洞,挖洞的过程中判断挖完洞的宫格是否有唯一解答,当然不追求唯一解答也可以,只要有解也是一种选择。
话不多说,直接打码。
首先需要在类中添加一个成员变量,用于挖洞过程中记录当前数独数据
class Sudoku:
def __init__(self):
# 初始化九宫格
self.grids = np.zeros((9, 9), dtype='i1', order='C')
self.possibleNums = {
1, 2, 3, 4, 5, 6, 7, 8, 9}
# 挖洞时保存挖好的数独
self.uniqueGrids = None
先给出挖洞后是否有唯一解的方法。
# 挖洞后判断是否该九宫格只有唯一的答案
def checkUnique(self, row, col):
for value in range(1, 10):
if self.grids[row, col] != value:
# 假设挖掉这个数字
self.grids[row, col] = 0
if value in self.get_possible(row, col):
# 更换一个数字之后检查是否还有另一解
self.grids[row, col] = value
if self.dfs():
return False
# 上面进行深度优先过程已经改变了 self.grids的值,恢复更换这个数字之前的状态
self.grids = self.uniqueGrids.copy()
# 已尝试所有其他数字发现无解,即只有唯一解
return True
下面就是挖洞方法了,参数level实际上从81个格子中挖掉的数量.。可以通过这个参数来设置难度。挖掉的数越多,难度相对来说。
def generateByDigHole(self, level):
# level 表示要挖掉的数字个数,通常挖掉 50 - 60 个左右,
# 最多挖去63-64个可以得到有唯一解的宫格,但是需要计算的时间会长很多
self.uniqueGrids = self.grids.copy()
digged = 0
while digged <= level:
row = random.randint(0, 8)
col = random.randint(0, 8)
# 该格子已经挖过了
if self.uniqueGrids[row, col] == 0:
continue
# 挖掉该格子后能生成唯一的九宫格。如果有就继续挖,如果没有唯一解就不挖这个格子
if self.checkUnique(row, col):
# 保存挖洞后的状态
self.uniqueGrids[row, col] = 0
# 挖完洞后继续挖,直到挖出指定数量的格子
self.grids = self.uniqueGrids.copy()
digged += 1
# 难度特别大时的副产品,
if digged >= 52:
print("After dig {}:".format(digged))
self.output_sudo()
好了,数独类就这样了。如果想测试一下性能,或者观察数独解答或挖洞过程中迭代的次数,可以添加相关的统计变量,然后在方法中相关位置添加就可以了。这里只是展示最基本的功能,个人感觉越简单、越清晰越好。
既然数独类已经完成,下面进行测试代码。
def main():
mySudoku = Sudoku()
# 在生成算法中,包括生成一个最终解以及挖洞。生成一个最终解由于采用的是随机算法,
# 因此分析起来比较复杂,不过将n取11的时候已经有99 % 概率生成正解了,
# 也就是99 % 的概率只需要尝试一次,因此不妨就设为O(V + E)。
while not mySudoku.lasVegas(11):
pass
mySudoku.output_sudo()
digCount = 56
# 通过测试发现,checkUnique并不能确保挖出来的宫格只有唯一解。
mySudoku.generateByDigHole(digCount)
mySudoku.output_sudo()
# 重新将挖洞后的数独利用深度优先算法解答
mySudoku.dfs()
mySudoku.output_sudo()
if __name__ == '__main__':
main()
经过我的测试,挖54个·洞生成一个有唯一解的数独在一秒内就可以完成。但是如果挖55个或以上的具有唯一解的数独需要的时间就慢慢变长了。如果要挖出60个以上的洞,那么可能需要喝杯茶才行。当然,有兴趣的朋友看看算法上有没有改善的空间,或者利用空间换时间的方法等。
下面给一个我挖57个洞的输出数据,共参考。
+-------+-------+-------+
| 1 | 5 | |
| | 3 | |
| | 4 | |
+-------+-------+-------+
| | | 8 |
| | 1 | |
| | | 6 |
+-------+-------+-------+
| | 2 | |
| | | |
| 7 | | 9 4 |
+-------+-------+-------+
根据初始11个数随机生成的数独
+-------+-------+-------+
| 1 2 3 | 8 9 5 | 4