目录
相关介绍
什么是数独?
数独是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复 。
数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。
什么是回溯法?
回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。使用回溯法的核心是记录每一步操作的位置,并在当前位置的数字均不符合条件时退回至上一位置。由此,可以使用栈来实现。
思路及实现
算法思路
首先,从第一行开始寻找第一个待填写的元素位置,并压入用于记录每次操作位置的栈中;然后,判断此时该位置元素是否合法(合法:即该数字位于0-9之间,并且与该数字所在行、所在列、所在宫内的数字均不重复)。当该数字合法时,则继续寻找下一个待填写的位置,并压入栈中;当该数字不合法时,若该数字还没有大于9则自增,若该数字已大于9则退回至上一位置(当前位置归零并弹出);最后,当最后一个待填写的元素合法且不等于0之前,重复执行上述步骤。
代码编写
设置记录空位置的栈
- 首先,声明列表。
#全局变量记录空位置(使用回溯法)
indexList = []
- 然后,实现入栈和出栈的方法。
# --- 入栈 --- #
def myPush(coordinate):
indexList.append(coordinate)
# --- 出栈 --- #
def myPop():
indexList.pop()
寻找第一个空元素位置
# --- 寻找第一个空元素位置 --- #
def firstEmpty():
for i in range(0,9):
for j in range(0,9):
if sudoku[i][j] == 0:
myPush([i,j])
return
寻找最后一个空元素位置
# --- 寻找最后一个空元素位置 --- #
def lastEmpty():
for i in range(8,-1,-1):
for j in range(8,-1,-1):
if sudoku[i][j] == 0: #'0'
return [i,j]
位置判空加入栈中
# --- 位置判空加入栈中 --- #
def isEmpty():
#获取栈顶元素
a,b = indexList[-1]
#从下一元素开始找起
b += 1
for i in range(a,9):
if i != a:
b = 0
for j in range(b,9):
if sudoku[i][j] == 0:
myPush([i,j])
return
当前位置自增
# --- 当前位置自增 ---#
def myAdd():
#获取栈顶元素
x,y = indexList[-1]
sudoku[x][y] += 1
判断当前位置是否合法
# --- 判断当前位置是否合法 --- #
def isLegal():
#获取栈顶元素
x,y = indexList[-1]
temp = sudoku[x][y]
#判断该行是否重复
for i in range(9):
if sudoku[x][i] == temp and i != y:
return False; #当前位置不合法
#判断该列是否重复
for i in range(9):
if sudoku[i][y] == temp and i != x:
return False; #当前位置不合法
#判断该宫是否重复
xx = int(x / 3)
yy = int(y / 3)
for i in range(3):
for j in range(3):
if sudoku[xx * 3 + i][yy * 3 + j] == temp and (xx * 3 + i) != x and (yy * 3 + j) != y:
return False; #当前位置不合法
return True #当前位置合法
判断某一位置是否合法 (用于判断末尾元素)
# --- 判断某一位置是否合法 --- #
def oneIsLegal(x,y):
temp = sudoku[x][y]
#判断该行是否重复
for i in range(9):
if sudoku[x][i] == temp and i != y:
return False; #当前位置不合法
#判断该列是否重复
for i in range(9):
if sudoku[i][y] == temp and i != x:
return False; #当前位置不合法
#判断该宫是否重复
xx = int(x / 3)
yy = int(y / 3)
for i in range(3):
for j in range(3):
if sudoku[xx * 3 + i][yy * 3 + j] == temp and (xx * 3 + i) != x and (yy * 3 + j) != y:
return False; #当前位置不合法
return True #当前位置合法
回退函数
# --- 回退函数 --- #
def myReturn():
#获取栈顶元素
x,y = indexList[-1]
sudoku[x][y] = 0
myPop()
输出数独
# --- 输出数独 --- #
def myOutput():
print("答案为:")
for i in range(9):
print(sudoku[i])
主程序
# --- 程序入口 --- #
def main():
#寻找首个空元素
firstEmpty()
#寻找末尾空元素
last = lastEmpty()
m,n = last
#当末尾空元素合法且不等于0时停止循环
while oneIsLegal(m,n) != True or sudoku[m][n] == 0:
#获取栈顶元素
x,y = indexList[-1]
#判断当前位置是否合法
if isLegal() == True and sudoku[x][y] <= 9 and 0 < sudoku[x][y]:
#寻找下一个空元素
isEmpty()
else:
#不合法 - 回退
if sudoku[x][y] >= 9:
myReturn()
myAdd()
else: #不合法 - 自增
myAdd()
myOutput()
if __name__ == "__main__":
main()
测试数独
#储存数独元素
sudoku = [[8,0,0,0,0,0,0,0,0],
[0,0,3,6,0,0,0,0,0],
[0,7,0,0,9,0,2,0,0],
[0,5,0,0,0,7,0,0,0],
[0,0,0,0,4,5,7,0,0],
[0,0,0,1,0,0,0,3,0],
[0,0,1,0,0,0,0,6,8],
[0,0,8,5,0,0,0,1,0],
[0,9,0,0,0,0,4,0,0],
]
运行结果
完整代码
#全局变量记录空位置(使用回溯法)
indexList = []
#储存数独元素
sudoku = [[8,0,0,0,0,0,0,0,0],
[0,0,3,6,0,0,0,0,0],
[0,7,0,0,9,0,2,0,0],
[0,5,0,0,0,7,0,0,0],
[0,0,0,0,4,5,7,0,0],
[0,0,0,1,0,0,0,3,0],
[0,0,1,0,0,0,0,6,8],
[0,0,8,5,0,0,0,1,0],
[0,9,0,0,0,0,4,0,0],
]
# --- 程序入口 --- #
def main():
#寻找首个空元素
firstEmpty()
#寻找末尾空元素
last = lastEmpty()
m,n = last
#当末尾空元素合法且不等于0时停止循环
while oneIsLegal(m,n) != True or sudoku[m][n] == 0:
#获取栈顶元素
x,y = indexList[-1]
#判断当前位置是否合法
if isLegal() == True and sudoku[x][y] <= 9 and 0 < sudoku[x][y]:
#寻找下一个空元素
isEmpty()
else:
#不合法 - 回退
if sudoku[x][y] >= 9:
myReturn()
myAdd()
else: #不合法 - 自增
myAdd()
myOutput()
# --- 寻找第一个空元素位置 --- #
def firstEmpty():
for i in range(0,9):
for j in range(0,9):
if sudoku[i][j] == 0: #'0'
myPush([i,j])
return
# --- 寻找最后一个空元素位置 --- #
def lastEmpty():
for i in range(8,-1,-1):
for j in range(8,-1,-1):
if sudoku[i][j] == 0: #'0'
return [i,j]
# --- 位置判空加入栈中 --- #
def isEmpty():
#获取栈顶元素
a,b = indexList[-1]
#从下一元素开始找起
b += 1
for i in range(a,9):
if i != a:
b = 0
for j in range(b,9):
if sudoku[i][j] == 0: #'0'
myPush([i,j])
return
# --- 入栈 --- #
def myPush(coordinate):
indexList.append(coordinate)
# --- 出栈 --- #
def myPop():
indexList.pop()
# --- 当前位置自增 ---#
def myAdd():
#获取栈顶元素
x,y = indexList[-1]
sudoku[x][y] += 1
# --- 判断当前位置是否合法 --- #
def isLegal():
#获取栈顶元素
x,y = indexList[-1]
temp = sudoku[x][y]
#判断该行是否重复
for i in range(9):
if sudoku[x][i] == temp and i != y:
return False; #当前位置不合法
#判断该列是否重复
for i in range(9):
if sudoku[i][y] == temp and i != x:
return False; #当前位置不合法
#判断该宫是否重复
xx = int(x / 3)
yy = int(y / 3)
for i in range(3):
for j in range(3):
if sudoku[xx * 3 + i][yy * 3 + j] == temp and (xx * 3 + i) != x and (yy * 3 + j) != y:
return False; #当前位置不合法
return True #当前位置合法
# --- 判断某一位置是否合法 --- #
def oneIsLegal(x,y):
temp = sudoku[x][y]
#判断该行是否重复
for i in range(9):
if sudoku[x][i] == temp and i != y:
return False; #当前位置不合法
#判断该列是否重复
for i in range(9):
if sudoku[i][y] == temp and i != x:
return False; #当前位置不合法
#判断该宫是否重复
xx = int(x / 3)
yy = int(y / 3)
for i in range(3):
for j in range(3):
if sudoku[xx * 3 + i][yy * 3 + j] == temp and (xx * 3 + i) != x and (yy * 3 + j) != y:
return False; #当前位置不合法
return True #当前位置合法
# --- 回退函数 --- #
def myReturn():
#获取栈顶元素
x,y = indexList[-1]
sudoku[x][y] = 0
myPop()
# --- 输出数独 --- #
def myOutput():
print("答案为:")
for i in range(9):
print(sudoku[i])
if __name__ == "__main__":
main()