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

Python中类数独行无重复排列的高效生成方案求助

高效生成类数独合法行组合的实现方案

你的核心问题是避免先生成所有可能组合再过滤,而是在生成过程中就排除重复数字,这里用回溯法是最优解——在每一步选择数字时,只选当前位置候选集中未被使用过的数字,从根源上避免无效组合的生成。

基础回溯实现

先统一处理输入(把确定值转为单元素候选集),再通过递归逐个位置选择合法数字,过程中维护已使用的数字集合,确保不重复:

def get_valid_row_combinations(row):
    # 预处理:将所有元素转为候选集合,同时检查初始冲突
    processed = []
    used_fixed = set()
    for item in row:
        if isinstance(item, (int, float)):
            num = int(item)
            if num in used_fixed:
                return []  # 存在重复的确定值,无合法组合
            processed.append({num})
            used_fixed.add(num)
        else:
            # 列表/集合转成集合,去重候选值并排除已占用数字
            candidates = set(item) - used_fixed
            if not candidates:
                return []  # 当前位置无可用候选,无合法组合
            processed.append(candidates)
    
    result = []
    
    def backtrack(pos, used, path):
        if pos == len(processed):
            result.append(tuple(path))
            return
        # 遍历当前位置的候选,且未被使用过的数字
        for num in processed[pos] - used:
            backtrack(pos + 1, used | {num}, path + [num])
    
    backtrack(0, used_fixed, [])
    return result

# 测试示例1
row1 = [[1, 2, 3, 4], [1, 2, 3], [1, 2, 3, 6], [1, 2, 4], [1, 3, 4, 5, 6], [2, 3, 4, 5]]
print(len(get_valid_row_combinations(row1)))  # 输出32,和原方法结果一致

# 测试示例2
row2 = [[1, 2, 3], [1, 2, 3], 5, 6, [1, 3, 4], [2, 3, 4]]
print(len(get_valid_row_combinations(row2)))

优化:启发式回溯(优先处理候选少的位置)

如果行中某些位置候选集很小(比如只有1个选项),优先处理这些位置可以更早剪枝,大幅减少递归次数。实现思路是先对位置按候选集大小排序,记录原始索引,最后再把结果还原回原始顺序:

def get_valid_row_combinations_optimized(row):
    # 预处理,同时记录原始索引
    processed = []
    used_fixed = set()
    for idx, item in enumerate(row):
        if isinstance(item, (int, float)):
            num = int(item)
            if num in used_fixed:
                return []
            processed.append(({num}, idx))
            used_fixed.add(num)
        else:
            candidates = set(item) - used_fixed
            if not candidates:
                return []
            processed.append((candidates, idx))
    
    # 按候选集大小升序排序,优先处理候选少的位置
    processed.sort(key=lambda x: len(x[0]))
    
    result = []
    
    def backtrack(pos, used, path_map):
        if pos == len(processed):
            # 还原回原始顺序
            final_path = [None] * len(row)
            for num, idx in path_map.items():
                final_path[idx] = num
            result.append(tuple(final_path))
            return
        candidates, idx = processed[pos]
        for num in candidates - used:
            # 记录数字对应的原始索引
            new_path = path_map.copy()
            new_path[num] = idx
            backtrack(pos + 1, used | {num}, new_path)
    
    backtrack(0, used_fixed, {})
    return result

# 测试优化版本
print(len(get_valid_row_combinations_optimized(row1)))  # 同样输出32,但效率更高

为什么比原方法高效?

原方法用itertools.product会生成所有笛卡尔积(比如你的例子是4×3×4×3×5×4=2880个),再过滤掉重复的;而回溯法在每一步只选择未使用过的数字,完全不会生成无效组合,对于候选集约束越强的场景,效率提升越明显。

比如你的第一个示例,回溯法只会生成32个合法组合对应的分支,不会浪费资源在重复数字的组合上。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 17:15:05