使用Python进行数独求解详解(一)

作者:赵卓不凡 时间:2023-12-25 09:39:20 

1. 引言

本文为介绍流行的数独游戏的系列文章中的第一篇。更具体地说,我们如何构建一个脚本来解决数独难题,本文的重点在于介绍用于构建数独求解器的回溯算法。

使用Python进行数独求解详解(一)

数独这个名字的由来来自日语短语suuji wa dokushin ni kagiru,意思是“数字必须保持单一”。数独游戏的流行也源于其规则的简单性:数独游戏要求在 9 x 9 空间的网格上进行数字填写。在行和列中有 9 个“正方形”的格子block(由 3 x 3 个子单元格cell组成)。每一行、每一列、每一个block都需要填写数字 1-9,行、列、block内不得重复任何数字。

好的,知道了上述数独的规则,那么我们就来直接进入主体吧。 :)

2.描述数独九宫格

这一步主要为使用一组数字来初始化我们的九宫格。我们使用setBoard() 函数将输入转换为适合我们后续操作的数据类型。我们使用以下约定:

  • 空的单元格cell初始化为默认值0。

  • 维持数独谜题数字值的数据类型是一个 9x9 大小的二维列表。

这里我们的输入是一个多行字符串,我们将其处理成二维列表的形式。样例代码如下:

# Initialize a 2-D list with initial values described by the problem.
# Returns board
def setBoard():
   board = list()
   sudokuBoard = '''
   200080300
060070084
030500209
000105408
000000000
402706000
301007040
720040060
004010003
'''
   rows = sudokuBoard.split('\n')
   for row in rows:
       column = list()
       for character in row:
           digit = int(character)
           column.append(digit)
       board.append(column)
   return board

上述代码运行后,如果展示在拼图游戏中,他的样子大概如下:

使用Python进行数独求解详解(一)

3.寻找下一个空子单元格

函数findEmpty() 函数的操作更加简单:对初始化后的九宫格作为参数传递,然后该遍历该九宫格中每一个子单元格cell,直到找到返回的第一个空的子单元格。如果没有找到空的子单元格,这表明我们的问题已解决,因此它返回None。

样例代码如下:

# Find next empty space in Sudoku board and return dimensions
def findEmpty(board):
   for row in range(9):
       for col in range(9):
           if board[row][col] == 0 :
               return row,col
   return None

4. 子单元格中设置值的合法性

函数isValid()的操作是确认设定的数字是否是九宫格子单元格的有效选项。为了使设置的值满足数独九宫格的要求,该值的设置需满足以下条件:

  • 同一行的任何子单元格cell都不应包含该数字

  • 同一列的任何子单元格cell都不应包含该数字

  • 该子单元格cell所在的block不应该包含该数字

如果我们设定的值满足以上所有条件,该函数返回True,否则返回False。代码如下:

# Check whether a specific number can be used for specific dimensions
def isValid(board, num, pos):
   row, col = pos
   # Check if all row elements include this number
   for j in range(9):
       if board[row][j] == num:
           return False
   # Check if all column elements include this number
   for i in range(9):
       if board[i][col] == num:
           return False
   # Check if the number is already included in the block
   rowBlockStart = 3* (row // 3)
   colBlockStart = 3* (col // 3)

rowBlockEnd = rowBlockStart + 3
   colBlockEnd = colBlockStart + 3
   for i in range(rowBlockStart, rowBlockEnd):
       for j in range(colBlockStart, colBlockEnd):
           if board[i][j] == num:
               return False

return True

以下是通过isValid() 函数中描述的条件后成功插入新值的动态示例:

使用Python进行数独求解详解(一)

同时,若我们引入一个与九宫格数独上已经存在的值冲突的数值,那么此时该值将不会 * 入。图例如下:

使用Python进行数独求解详解(一)

5. 实现回溯算法

下一个函数是我们整个解决方案的核心思想,这里引入了回溯思想,该算法的实现步骤如下:

  • 搜索下一个空的子单元格cell。如果没有找到,那么我们已经解决了这个难题;如果没有,则转到第 2 步。

  • 通过迭代数字1-9 来猜测正确的数字,并参考已经确定的数字来检查它是否是一个合法的数字。

  • 如果找到一个有效的数字,此时递归调用solve() 函数并猜测下一个空的子单元格cell。

  • 如果数字1-9均无效,则将将子单元格cell的值重置为 0,并继续迭代以查找下一个有效数字。

# Solve Sudoku using backtracking
def solve(board):
   blank = findEmpty(board)
   if not blank:
       return True
   else:
       row, col = blank
   for i in range(1,10):
       if isValid(board, i, blank):
           board[row][col] = i

if solve(board):
               return True

board[row][col] = 0
   return False

由于递归理解起来不是那么直观,不妨让我们尝试使用动态来将整个过程形象化:

使用Python进行数独求解详解(一)

正如我们在上面的示例中看到的那样,该算法用有效数字填充空子单元格cell,直到出现死胡同;然后它回溯,直到重新迭代该过程。

6. 性能表现

上述我们的程序需要使用回溯算法来遍历每个单元格的许多潜在值。这当然不是最优的解法,可以预想到我们的解决方法的性能会很慢。

我们使用上述代码,来解决欧拉计划的第96题中的50道数独题目,运行时间为:

The execution time of above program is : 23.56185507774353 s

好吧,确实有点慢。我们后面再来开篇讲解数独算法的优化。

7. 总结

本文讲解了数独游戏的相关规则,以及如何在Python中利用回溯思想来一步一步解决数独问题,并给出了完整的实现。

来源:https://blog.csdn.net/sgzqc/article/details/122992189

标签:Python,数独
0
投稿

猜你喜欢

  • Python使用captcha库制作带参数输入验证码案例

    2022-01-25 20:26:03
  • 正确理解python中的关键字“with”与上下文管理器

    2022-10-26 18:05:02
  • ASP开发中有用的函数(function)集合(3)

    2008-10-14 17:20:00
  • PHP获取网页标题的3种实现方法代码实例

    2024-05-03 15:51:06
  • 在python中按照特定顺序访问字典的方法详解

    2023-02-09 07:14:34
  • django项目环境搭建及在虚拟机本地创建django项目的教程

    2022-10-14 14:04:32
  • Git下载与环境变量配置方法

    2022-10-19 19:53:13
  • python-opencv 中值滤波{cv2.medianBlur(src, ksize)}的用法

    2023-05-07 03:01:23
  • php实现pdo数据库操作类过程详解

    2023-05-25 11:15:05
  • python实现360皮肤按钮控件示例

    2023-01-22 11:19:58
  • MySQL 存储过程中执行动态SQL语句的方法

    2024-01-12 21:22:22
  • Python控制浏览器自动下载歌词评论并生成词云图

    2022-04-17 11:24:56
  • python保留小数位的三种实现方法

    2021-09-05 22:36:09
  • Go系列教程之反射的用法

    2024-04-25 15:25:32
  • windows下mysql 8.0.27 安装配置图文教程

    2024-01-24 00:26:57
  • 解决python对齐错误的方法

    2023-08-11 05:40:07
  • PYQT5开启多个线程和窗口,多线程与多窗口的交互实例

    2023-07-19 04:21:21
  • Python真题案例之最长回文子串 周期串详解

    2021-12-01 02:57:02
  • Python Django教程之实现天气应用程序

    2022-03-22 23:02:43
  • Python标准库calendar的使用方法

    2023-09-01 01:28:07
  • asp之家 网络编程 m.aspxhome.com