avatar

Xiaojun Wu

Ph.D. Candidate, HKUST(GZ)
Affiliate LLM Researcher, IDEA Research
wuxiaojun@idea.edu.cn & wuxiaojun@pku.edu.cn & xwu647@connect.hkust-gz.edu.cn


← Blog

python数学建模实践

数独问题的求解与可能的现实意义:以列车调度问题为例

在知乎查看原文

阅读 知乎未公开 收藏 12 点赞 3

互动数据为 2026-09-13 的知乎数据快照;阅读量未在当前可用的知乎公开接口中提供。

最近听说有朋友在做数独游戏,因此探索了一下这个游戏。找了下网上的内容基本都只强调数独游戏的益智性和对于孩子专注性的训练,难道这个数学游戏是不是和其他数学模型一样可以被应用于现实生活中(我认为这是很重要的,数学抽象于现实生活又能够应用于现实生活生产活动中,这就是数学建模的意义不是吗)?现实生活中有哪些问题可以被建模成数独模型吗?最终也是发现确实某些情况下列车调度问题是可以被建模成数独模型的,这样求解时可以采用经典的启发式策略求解数独模型,也发现其实也是可以建模成一个整数规划问题的。

一、数独游戏和数独游戏的python求解

数独是一种逻辑游戏,其中数字需要填入网格,使每行、每列和每个小格子中的数字均为1到n(在经典的9×9数独中,n=9)。在此情况下,我们将使用4×4的数独,因此n=4。在4×4数独中,不仅行和列中的数字1至4都不能重复,2×2的宫(或称为子网格、区域或盒子)中的数字也必须是1至4,且不重复。这些2×2的宫是不重叠的。

在给定一些初始值的情况下,找到整个数独网格的可行解。数独一般的求解思路是,先找到可能值最少的格子进行求解(也就是约束最多的格子,比如说一行中已经有3个确定值,那另外一格的候选数字就只有1个;已经有2个确定值,另外两个格子的候选值都有2个,此时需要再结合列上的情况和宫上的情况,继续缩小某个格子的候选值)。还有一个约束就是相邻两个宫内,第一宫第一行的数字和第二宫第二行的数字一般是同一组。接下来我们按照这个思路写一个44数独的求解(代码只要自己稍微修改下就可以处理9*9问题了):

def is_valid(board, row, col, num):
    # Check row and column
    for i in range(4):
        if board[row][i] == num or board[i][col] == num:
            return False

    # Check 2x2 box
    startRow, startCol = 2 * (row // 2), 2 * (col // 2)
    for i in range(2):
        for j in range(2):
            if board[i + startRow][j + startCol] == num:
                return False

    return True
    
def find_empty_location(board):
    options = {}
    
    for i in range(4):
        for j in range(4):
            if board[i][j] == 0:
                possible_values = {num for num in range(1, 5) if is_valid(board, i, j, num)}
                options[(i, j)] = possible_values

    return options

def solve_sudoku(board):
    options = find_empty_location(board)

    if not options:
        return True

    # Find cell with minimum possible values
    cell = min(options, key=lambda k: len(options[k]))
    i, j = cell

    for num in options[cell]:
        board[i][j] = num
        if solve_sudoku(board):
            return True
        board[i][j] = 0

    return False

sudoku_board = [
    [0, 0, 1, 0],
    [0, 0, 0, 3],
    [4, 0, 0, 0],
    [0, 2, 0, 0]
]

if solve_sudoku(sudoku_board):
    for row in sudoku_board:
        print(row)
else:
    print("No solution exists!")

二、现实问题建模为数独模型:以列车调度问题为例

假设有4个火车站,分别命名为A、B、C和D。对于列车调度问题映射到4×4数独的场景,我们可以这样设定:

  • 行:代表时间段。例如,一天可以被分为4个时间段(如早晨、中午、下午、晚上)。
  • 列:代表火车站。即A、B、C、D四个站。
  • 单元格中的数字:代表该时间段内从哪个火车站发出的列车到达。例如,1代表从A站发出的列车,2代表从B站发出的列车,以此类推。
  • 2×2的宫:可以看作是某种特殊的时间段或某些特殊的火车站的组合。例如,左上角的2×2宫可以代表早晨和中午时间段的A和B两个火车站的组合。

这样的一个可能的调度方案如下:

在这个模型中,每个2×2宫中都有1至4的数字,而且不重复,确保在这些特殊的时间段或火车站组合中,列车调度不会有冲突。

以左上角的2×2宫为例:

  • 在早晨时段的A站,有从B站出发的列车到达。
  • 在早晨时段的B站,有从D站出发的列车到达。
  • 在中午时段的A站,有从C站出发的列车到达。
  • 在中午时段的B站,有从A站出发的列车到达。

这样的设定满足了列车调度的需求(也就是说,以A站为例,每天都有来自B/C/D的车,以第一行为例,每个时间段都有至少有),并且保证了每个火车站在每个时间段都有不同出发地的列车到达(也就是说上半天时段,A,B站必须有来自C,D站的车)。

当然这个例子也不是完美的符合数独模型,现实中也少有问题完美符合数独模型,只能是近似的思路。比如第4行第一列出现1,代表A车站晚上有来自A车站的车?这可能是说不太通的,当然也可以理解为A车站晚上休息。同理就是B车站下午休息,C车站中午休息,D车站也晚上休息。看作这个是一个排班表。2*2宫的约束也是哦我们强加的一个约束,不一定在现实中实用。


三、试试建模为规划问题来求解数独模型吧

若我们要用数学规划的方式来建模这个问题,我们可以定义一些决策变量、目标函数和约束条件。不过,我想强调的是数独通常不是用规划来解决的,因为它是一个组合优化问题。但是,列车调度问题可以适用于整数线性规划模型。

假设我们用 \(x_{ijk}\) 作为决策变量,其中i和j分别表示行和列的索引(例如,时间段和火车站),而k表示数值(也就是在特定时间和火车站到达的列车的出发站)。

这里  \(x_{ijk} \in \{0,1\}\) ,当且仅当在第i个时间段、第j个火车站到达的列车来自第k个出发站时,它的值为1。否则,为0。

接下来,我们可以建立以下约束条件:

1. 每个单元格只能有一个值:

\[ \sum_{k=1}^{4} x_{ijk} = 1, \forall i, j \]

2. 每行中的每个数字只能出现一次:

\[ \sum_{j=1}^{4} x_{ijk} = 1, \forall i, k \]

3. 每列中的每个数字只能出现一次:

\[ \sum_{i=1}^{4} x_{ijk} = 1, \forall j, k \]

4. 每个2×2宫格中的每个数字只能出现一次:

\[ \sum_{i \in I_m}\sum_{j \in J_n} x_{ijk} = 1, \forall k, m, n \]

其中, \(I_m\) 和  \(J_n\) 分别是第m个和第n个2×2宫格中的行和列的索引。

其中,约束条件1确保每个单元格只有一个数值,约束条件2和3确保每个数字在每行和每列中只出现一次,约束条件4确保每个数字在每个2×2宫格中只出现一次。通常我们可以设置目标函数为最小化或最大化某些成本或利润。但在这个列车调度的例子里,我们主要关心的是满足约束条件,因此目标函数可以仅仅是最小化或最大化某个决策变量,或者简单地找到一个可行解。比如我们设置一个目标函数,使得总利润最大。

假设每辆列车从一个站到另一个站带来的利润为已知,并存储在一个利润矩阵中,我们记这个利润矩阵为P,其中 \(P_{kj}\) 代表从第k个站到第j个站的列车带来的利润那么(注意k=j时利润是0,因为代表车站休息了),我们的目标函数将是:

\[ \text{Maximize } Z = \sum_{i=1}^{4}\sum_{j=1}^{4}\sum_{k=1}^{4} P_{kj} x_{ijk} \]

这个目标函数表示在不同的时间段 i,第 j 站接收第 k 站的列车所带来的总利润。我们希望选择 \(x_{ijk}\) 的值以最大化这个总利润。

这个模型就可以用整数线性规划求解或者遗传算法来解决。不过,由于数独的本质是寻找可行解的问题,通常可以用回溯算法或最前面的python启发式算法来解决就行。


© 2025 Xiaojun Wu. All rights reserved. Powered by Jekyll and Minimal Light theme.