python回溯法解9*9数独


前言

看了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内容挺少,难点在于回退的操作。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值