文章目录
前言
看了labuladong算法小抄里的回溯法,照着模板,加入自己的理解,用python做了个解9*9数独的回溯法,。
一、回溯法解数独代码
直接上代码:
import copy
import time
# 生成一个表,放置每行还能放的数字,排除原数独表每行放置的数字
def minusTable(table_t, ori_table2):
table2 = copy.deepcopy(table_t)
for i2 in range(9):
for j2 in range(9):
if ori_table2[i2][j2] in table2[i2]:
table2[i2].remove(ori_table2[i2][j2])
return table2
def backtrack(remain_table_t, current_table_t, row, col):
# global return_flag
# 给解决方式计数
global solve
global start
# 记录原来的行列,回退需要
ori_row = row
ori_col = col
# 结束条件,整个表全填完,row变成9
if row == 9:
solve += 1
print("\nSolve %s :" % solve)
for line in current_table_t:
print(line)
print("\n耗时: %s" % (time.time() - start))
start = time.time()
return
# 如果某一格子数字不为0,即已存在数字,往下一格子,这里应该算路径问题,而不是做选择的问题
if current_table_t[row][col] != 0:
row = row + int((col + 1) / 9)
col = (col + 1) % 9
backtrack(remain_table_t, current_table_t, row, col)
# 整个方法有两个地方调用backtrack,此其一,回到此处,说明已经找到其中一种解要么还没找到,要继续找,return,不然往下走了;这里不好理解,我也觉得悬。。
# 与后面的不同,后面调用在循环里会进行下一次遍历。
return
# 记录原来的路径,回退需要
remain_table_next = copy.deepcopy(remain_table_t)
current_table_next = copy.deepcopy(current_table_t)
# 为每行可选数字的其中一个,遍历
for num in remain_table_t[row]:
if not checkNum(current_table_next, row, col, num):
continue
# 做选择,格子添加数字
current_table_next[row][col] = num
remain_table_next[row].remove(num)
# backtrack(选择列表,路径) ,row和col相当于当前路径吧,remai_table相当记录了可选列表。
backtrack(remain_table_next, current_table_next, row + int((col + 1) / 9), (col + 1) % 9)
# 撤销选择,回退路径和选择列表
remain_table_next = copy.deepcopy(remain_table_t)
current_table_next = copy.deepcopy(current_table_t)
row = ori_row
col = ori_col
def checkNum(current_table_next_t, row, col, num):
checkTable = copy.deepcopy(current_table_next_t)
checkTable[row][col] = num
# checkCol检查列。(行是不用检查的,因为行作为选择列表,从其中一个一个抽出来)
col_list = list(checkTable[x][col] for x in range(9))
while 0 in col_list:
col_list.remove(0) # 移除0
if len(col_list) != len(set(col_list)):
return False # 如果重复了长度就不一样
# checkBlock
# 获取当前row,col对应块(3*3)的block_row,block_col
block_row = int(row / 3)
block_col = int(col / 3)
block_list = []
# 获取当前block的9个元素
for r in range(block_row * 3, (block_row + 1) * 3):
for c in range(block_col * 3, (block_col + 1) * 3):
block_list.append(checkTable[r][c])
# 同checkCol
while 0 in block_list:
block_list.remove(0)
if len(block_list) != len(set(block_list)):
return False
return True
if __name__ == '__main__':
# 构造空表
table = [[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]]
# 将空表添加1-9,在后边辅助生成每行还能填入的值(去除已经填的值)
for i in range(9):
for j in range(9):
table[i][j] = j % 9 + 1
# 目标数独表
ori_table = [[0, 0, 2, 3, 0, 0, 0, 0, 0],
[0, 4, 1, 2, 0, 0, 0, 0, 0],
[8, 0, 0, 0, 1, 0, 0, 0, 6],
[6, 0, 0, 0, 0, 0, 0, 5, 9],
[0, 0, 3, 0, 0, 0, 4, 0, 0],
[5, 2, 0, 0, 0, 0, 0, 0, 3],
[7, 0, 0, 0, 9, 0, 0, 0, 4],
[0, 0, 0, 0, 0, 6, 8, 9, 0],
[0, 0, 0, 0, 0, 4, 5, 0, 0]]
# remain_table表格放置每行还能放的数字
remain_table = minusTable(table, ori_table)
current_table = ori_table
# return_flag = False#可以不用,
solve = 0
start = time.time()
backtrack(remain_table, current_table, 0, 0)
总结
代码按着模板来的,解释有点多,其实batrack内容挺少,难点在于回退的操作。