定义
数独是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复 。
数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。
算法思想
上一篇文章利用了深度优先搜索,递归实现了解决数独问题。现考虑其他算法思想解决数独问题。
摒弃法:摒弃法也称等位群格位算法,是模拟人求解数独时的思维方法。按照行、列、粗线格唯一值的原则,将现有三个方向已有数据分步排除抛弃的方法,剩下数即为所填数。
缺点:只能解决简单数独,对于复杂数独,即一个位置可能有多个解并且解不是所有可填数中最小的数时才能得到结果。
# -*- coding: utf-8 -*-
# @Time : 2022/4/30 17:10
# @Author : kunkun
# @File : algorithm2.py
# ------------------------------
# 数独算法2,摒弃法,只能求解简单数独
# 算法思想:摒弃法也称等位群格位算法,是模拟人求解数独时的思维方法。
# 照行、列、粗线格唯一值的原则,将现有三个方向已有数据分步排除抛弃的方法,剩下数即为所填数。
import numpy as np
sudoku = np.zeros((9, 9), dtype=np.int32) # 存储数独矩阵
mark = np.zeros((81, 11), dtype=np.int32) # 标记数组,mark[i][0]与sudoku元素一一对应,mark[i][i]-mark[i][9]存储1~9,mark[10]标记是否得出解
count = 0 # 记录已填空的数量
def ini():
global count
global sudoku
sudoku = [[0, 0, 0, 0, 0, 0, 0, 0, 4],
[0, 2, 8, 7, 5, 0, 0, 3, 0],
[0, 3, 1, 0, 0, 2, 0, 0, 0],
[6, 5, 0, 2, 3, 7, 0, 0, 0],
[0, 0, 0, 0, 9, 0, 0, 0, 0],
[0, 0, 0, 6, 8, 1, 0, 5, 9],
[0, 0, 0, 9, 0, 0, 8, 6, 0],
[0, 9, 0, 0, 2, 4, 3, 1, 0],
[5, 0, 0, 0, 0, 0, 0, 0, 0]]
for i in range(81): # 初始化mark数组
if sudoku[i // 9][i % 9] != 0:
count += 1 # 记录count
mark[i][0] = sudoku[i // 9][i % 9]
mark[i][10] = 1
for j in range(1, 10):
mark[i][j] = j
# print(count)
def bqf():
global count
while count < 81:
count_old = count
for i in range(81):
if mark[i][10] == 0:
# print('i', i)
# 去除行中出现的数字
for j in range(9):
if 1 <= sudoku[i // 9][j] <= 9:
mark[i][sudoku[i // 9][j]] = 0
# 去除列中重复数字
for j in range(9):
if 1 <= sudoku[j][i % 9] <= 9:
mark[i][sudoku[j][i % 9]] = 0
# 去除九宫格中的重复数字
start_row = i // 9 // 3
start_cul = i % 9 // 3
# print('start', start_row, start_cul)
for j in range(start_row * 3, start_row * 3 + 3):
for k in range(start_cul * 3, start_cul * 3 + 3):
if 1 <= sudoku[j][k] <= 9:
mark[i][sudoku[j][k]] = 0
# print(mark[i])
# 计算剩余可填数字的个数
tmp = 0
index = 0
for j in range(1, 10):
if mark[i][j] == 0:
tmp += 1
else:
index = j
if tmp == 8:
mark[i][0] = index
sudoku[i // 9][i % 9] = index
mark[i][index] = 0
mark[i][10] = 1
count += 1
# print(i // 9, i % 9, index)
# print('tmp', tmp)
# print('index', index)
if count == count_old:
print("No Simple Solution")
break
if __name__ == '__main__':
ini()
bqf()
for i in range(9):
print(sudoku[i])
总结
摒弃法比递归的方法更符合人类的思维模式,但是只能解决简单数独,这是一个问题。接下来,我将从摒弃法中得到灵感,改进摒弃法,实现可以解决非简单数独的版本。