用线性规划解数独问题

Posted bkzy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用线性规划解数独问题相关的知识,希望对你有一定的参考价值。

数独游戏规则

数独游戏棋盘是一个9x9的格网,被划分成3x3个区域,每个区域是一块九宫格。玩家需要在格内填入1到9的数字,其中一些数字在游戏开始时已经给出。 要求:每一行,每一列,以及每一块小九宫格区域内的数字必须是唯一的,不允许出现重复数字。

可以使用python的pulp包解数独游戏。这个包是线性规划算法包

实现过程

import pulp as pl

# 值、行、列的变量范围列表
VALS = ROWS = COLS = range(1,10)

# 创建九宫格盒子,每一行为一个小九宫格
# k:小九宫格的行, l:小九宫格的列
# i: 大九宫格的行, j:大九宫格的列
Boxes = [
    [(3 * i + k + 1, 3 * j + l + 1) for k in range(3) for l in range(3)]
    for i in range(3)
    for j in range(3)
]
"""
程序运行后 Boxes的值为:
[[(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2), (3, 3)],
 [(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)],
 [(1, 7), (1, 8), (1, 9), (2, 7), (2, 8), (2, 9), (3, 7), (3, 8), (3, 9)],
 [(4, 1), (4, 2), (4, 3), (5, 1), (5, 2), (5, 3), (6, 1), (6, 2), (6, 3)],
 [(4, 4), (4, 5), (4, 6), (5, 4), (5, 5), (5, 6), (6, 4), (6, 5), (6, 6)],
 [(4, 7), (4, 8), (4, 9), (5, 7), (5, 8), (5, 9), (6, 7), (6, 8), (6, 9)],
 [(7, 1), (7, 2), (7, 3), (8, 1), (8, 2), (8, 3), (9, 1), (9, 2), (9, 3)],
 [(7, 4), (7, 5), (7, 6), (8, 4), (8, 5), (8, 6), (9, 4), (9, 5), (9, 6)],
 [(7, 7), (7, 8), (7, 9), (8, 7), (8, 8), (8, 9), (9, 7), (9, 8), (9, 9)]]
"""

# 创建问题
# 这个问题没有限定求最大还是求最小
prob = pl.LpProblem("数独")

# 创建二进制的数独变量
# 二进制变量 Choice_4_2_9 表示 4 这个数在第2行第9列是否出现了
# 如果这个变量的值为 1 ,则表示4出现在了第2行第9列
# 如果这个变量的值为 0 ,则表示4没有出现在第2行第9列
choices = pl.LpVariable.dicts("Choiece",(VALS,ROWS,COLS),cat=pl.LpBinary)

# 数据问题没有目标函数,只有约束
# 添加约束
for r in ROWS:
    for c in COLS:
        prob += pl.lpSum([choices[v][r][c] for v in VALS]) == 1 # 每个格中只能有一个值

for v in VALS:
    for r in ROWS:
        prob += pl.lpSum([choices[v][r][c] for c in COLS]) == 1 # 每行中每个值只能出现一次

    for c in COLS:
        prob += pl.lpSum([choices[v][r][c] for r in ROWS]) == 1 # 每列中每个值只能出现一次

    for b in Boxes:
        prob += pl.lpSum([choices[v][r][c] for (r, c) in b]) == 1 # 每个小九宫格中每个值只能出现一次

# 初始化起始数字,按照数独矩阵填写,没有数据的格填写0
input_data = [
    (5,3,0,0,7,0,0,0,0),
    (6,0,0,1,9,5,0,0,0),
    (0,9,8,0,0,0,0,6,0),
    (8,0,0,0,6,0,0,0,3),
    (4,0,0,8,0,0,0,0,1),
    (7,0,0,0,2,0,0,0,6),
    (0,6,0,0,0,0,2,8,0),
    (0,0,0,4,1,9,0,0,5),
    (0,0,0,0,8,0,0,7,9),
]
r = 0
for ar in input_data:
    r += 1
    c = 0
    for v in ar:
        c += 1
        if v > 0:
            prob += choices[v][r][c] == 1

# 求解问题
# prob.writeLP("Sudoku.lp") # 保存数独文件
prob.solve()

# 构建一个df矩阵,用于显示数据
import pandas as pd
sudokudf=pd.DataFrame([[i*j*0 for i in range(1,10)]for j in range(1,10)],index=range(1,10),columns=range(1,10))

# 将数字写入df矩阵,用于显示输出
for r in ROWS:
    for c in COLS:
        for v in VALS:
            if choices[v][r][c].value()==1:
                sudokudf.loc[r][c] = v

# 打印输出结果
display(sudokudf)

输出的结果

参考文献

https://coin-or.github.io/pulp/CaseStudies/a_sudoku_problem.html

以上是关于用线性规划解数独问题的主要内容,如果未能解决你的问题,请参考以下文章

用线性规划解数独问题

37解数独

C++用深度优先搜索解数独问题

软件工程基础个人个人项目 数独终局声称与解数独问题的控制台程序

Java 求解-解数独

python解数独