You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化数独求解器性能?递归实现性能提升求助

数独求解器性能优化方案

原代码的性能瓶颈并非递归本身,而是填充策略低效、候选值重复计算这两个核心问题,以下是针对性的优化方案:

核心优化点

1. 采用「最少剩余值(MRV)」启发式填充

原代码每次从左到右、从上到下找第一个空单元格填充,很容易提前走进无解分支,导致大量无效回溯。优先选择候选值最少的空单元格填充,能大幅减少回溯次数,这是数独求解器性能提升的关键。

2. 预维护行列块的已用数字集合

原代码每次计算候选值都要遍历整行、整列、整个3x3块,重复计算开销极大。改用三个数组分别记录每行、每列、每个3x3块的已用数字,填充/回溯时实时更新,候选值计算直接用全集减已用集合,效率提升明显。

3. 避免冗余的集合转换与列表操作

原代码中row_values + col_values + box_values的拼接和集合转换是不必要的开销,用预维护的已用集合直接计算差集,能节省大量时间。

优化后的代码

def solve_sudoku(board):
    # 初始化行、列、3x3块的已用数字集合
    rows = [set() for _ in range(9)]
    cols = [set() for _ in range(9)]
    boxes = [set() for _ in range(9)]
    empty_cells = []

    # 初始化已用数字集合和空单元格列表
    for r in range(9):
        for c in range(9):
            val = board[r][c]
            if val != 0:
                rows[r].add(val)
                cols[c].add(val)
                # 计算当前单元格所属的3x3块索引
                box_idx = (r // 3) * 3 + (c // 3)
                boxes[box_idx].add(val)
            else:
                empty_cells.append((r, c))

    def backtrack():
        if not empty_cells:
            return True
        
        # 找到候选值最少的空单元格(MRV启发式)
        min_options = 10
        target_cell = None
        for (r, c) in empty_cells:
            box_idx = (r // 3) * 3 + (c // 3)
            used = rows[r].union(cols[c]).union(boxes[box_idx])
            options = 9 - len(used)
            if options < min_options:
                min_options = options
                target_cell = (r, c)
                if min_options == 1:  # 提前终止,找到唯一候选的单元格
                    break
        
        r, c = target_cell
        box_idx = (r // 3) * 3 + (c // 3)
        used = rows[r].union(cols[c]).union(boxes[box_idx])
        candidates = set(range(1, 10)) - used

        for num in candidates:
            # 填充数字并更新已用集合
            board[r][c] = num
            rows[r].add(num)
            cols[c].add(num)
            boxes[box_idx].add(num)
            empty_cells.remove((r, c))

            if backtrack():
                return True
            
            # 回溯:恢复状态
            board[r][c] = 0
            rows[r].remove(num)
            cols[c].remove(num)
            boxes[box_idx].remove(num)
            empty_cells.append((r, c))
        
        return False

    backtrack()
    return board

# 测试用例
problem = [
    [9, 0, 0, 0, 8, 0, 0, 0, 1],
    [0, 0, 0, 4, 0, 6, 0, 0, 0],
    [0, 0, 5, 0, 7, 0, 3, 0, 0],
    [0, 6, 0, 0, 0, 0, 0, 4, 0],
    [4, 0, 1, 0, 6, 0, 5, 0, 8],
    [0, 9, 0, 0, 0, 0, 0, 2, 0],
    [0, 0, 7, 0, 3, 0, 2, 0, 0],
    [0, 0, 0, 7, 0, 5, 0, 0, 0],
    [1, 0, 0, 0, 4, 0, 0, 0, 7]
]

print(solve_sudoku(problem))

额外优化建议

  • 如果追求极致性能,可以加入「唯一候选数」「唯一位置」等约束传播规则,在回溯前先填充那些只有唯一解的单元格,进一步减少回溯次数。
  • 避免在empty_cells中频繁调用remove和append,可以改用索引标记的方式(比如用一个列表记录空单元格,用指针跟踪当前处理位置),不过对于9x9数独来说,当前方案的开销已经可以忽略。

内容的提问来源于stack exchange,提问作者profarvin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 05:57:30