启发式搜索算法解决数独问题sudoku,附python实现

定义

数独是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复 。
数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。

算法思想

启发式搜索算法,针对摒弃法的改进。
先算出所有空格的候选数,然后找到候选数个数最少的位置开始搜索。如果不满足则回退。

代码

# -*- coding: utf-8 -*-
# @Time    : 2022/5/2 14:58
# @Author  : kunkun
# @File    : algorithm3.py
# ------------------------------
# 启发式搜索算法,针对算法2的改进。
# 先算出所有空格的候选数,然后找到候选数个数最少的位置开始搜索。
import numpy as np

sudoku = np.zeros((9, 9), dtype=np.int32)  # 存储数独矩阵


def cal_blank():
    # 计算空格的个数
    count = 0
    coord = []
    for i in range(9):
        for j in range(9):
            if sudoku[i][j] == 0:
                coord_tmp = [i, j]  # 记录坐标
                coord.append(coord_tmp)
                count += 1
    return count, coord


def blank_candidate(coord, count):
    candidate_list = []  # 保存没哟个空格位置的候选列表
    for i in range(count):
        num = [1, 2, 3, 4, 5, 6, 7, 8, 9]
        # 取坐标
        x = coord[i][0]
        y = coord[i][1]
        # 找九宫格位置
        start_row = x // 3
        start_col = y // 3
        # 行去重复数字
        for j in range(9):
            if sudoku[x][j] in num:
                num.remove(sudoku[x][j])
        # 列去重复数字
        for j in range(9):
            if sudoku[j][y] in num:
                num.remove(sudoku[j][y])
        # 九宫格去重复数字
        for j in range(start_row * 3, start_row * 3 + 3):
            for k in range(start_col * 3, start_col * 3 + 3):
                if sudoku[j][k] in num:
                    num.remove(sudoku[j][k])
        candidate_list.append(num)
    return candidate_list


def min_index(candidate_list, coord, count):
    # 找到最小候选个数的位置
    candidate_len = []
    for i in range(count):
        lens = len(candidate_list[i])
        candidate_len.append(lens)
    min_len = min(candidate_len)  # 最小候选个数
    index = candidate_len.index(min_len)  # 最小候选个数索引
    return min_len, index, coord[index]


def search_algorithm():
    # 计算需要填空的坐标及个数:坐标列表:coord,个数:count
    count, coord = cal_blank()
    total_count = 0  # 记录总的迭代次数
    # 弹出的候选数列表
    pop_candi_list = []
    # 弹出坐标列表
    pop_coord_list = []
    while count:
        # 计算空白位置的候补列表
        candidate_tmp = blank_candidate(coord, count)
        # 计算最小候补个数的位置
        min_len, index, min_coord = min_index(candidate_tmp, coord, count)
        # print(total_count, candidate_tmp[index], min_coord)
        if min_len == 0:
            while len(pop_candi_list[-1]) == 0:
                # 上一个空格的坐标
                x = pop_coord_list[-1][0]
                y = pop_coord_list[-1][1]
                # 还原当前节点的值
                sudoku[x][y] = 0
                count = count + 1
                coord.append(pop_coord_list[-1])
                # 删除当前节点坐标及侯选数列表
                del pop_coord_list[-1]
                del pop_candi_list[-1]
            # 上一个空格位置重新填数
            x = pop_coord_list[-1][0]
            y = pop_coord_list[-1][1]
            sudoku[x][y] = pop_candi_list[-1][0]
            del pop_candi_list[-1][0]
            total_count += 1
        else:
            # 更改min_coord处的值
            x = min_coord[0]
            y = min_coord[1]
            sudoku[x][y] = candidate_tmp[index][0]
            # 取出min_coord位置处的候选数删除候选数的第一项后放到弹出来的候选数列表中
            tem_list = candidate_tmp.pop(index)
            del tem_list[0]
            pop_candi_list.append(tem_list)
            # 取出min_coord的位置放到弹出来的坐标列表中
            tem_coord = coord.pop(index)
            pop_coord_list.append(tem_coord)
            count = count - 1
            total_count += 1
    print(f'共迭代{total_count}次')


if __name__ == '__main__':
    sudoku = [[0, 0, 5, 3, 0, 0, 0, 0, 0],
              [8, 0, 0, 0, 0, 0, 0, 2, 0],
              [0, 7, 0, 0, 1, 0, 5, 0, 0],
              [4, 0, 0, 0, 0, 5, 3, 0, 0],
              [0, 1, 0, 0, 7, 0, 0, 0, 6],
              [0, 0, 3, 2, 0, 0, 0, 8, 0],
              [0, 6, 0, 5, 0, 0, 0, 0, 9],
              [0, 0, 4, 0, 0, 0, 0, 3, 0],
              [0, 0, 0, 0, 0, 9, 7, 0, 0]]
    search_algorithm()
    for i in range(9):
        print(sudoku[i])

总结

针对摒弃法的改进,考虑了一种启发式的搜索算法,核心思想仍然是模拟人的解题思路。为了跳出传统方法,下面将尝试运用一些新的方法解决数独问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>