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

Python中对称约束下大型数组的高效排列生成方法

高效生成含重复元素数组的唯一排列

你说的这个痛点太真实了——当数组里存在重复元素时,直接用itertools.permutations枚举所有排列完全是做无用功,会生成大量重复项,效率低到离谱,尤其是数组规模变大的时候根本没法用。比如你给出的数组[1, 1, 2, 3, 3, 3, 0, 0, 0, 0, 0],用permutations会生成11!(39916800)个排列,但实际唯一的排列数只有11!/(2! * 1! * 3! * 5!) = 27720,差距大得惊人。

先看看你提到的这段低效代码的问题:

from itertools import permutations
p = permutations([1, 1, 2, 3, 3, 3, 0, 0, 0, 0, 0])
j = 0
for i in list(p):
    print(i)
    j += 1

这段代码会把所有重复的排列都生成一遍,绝大多数计算都是浪费,完全没必要。下面给你两种高效的解决方案,核心思路都是从根源避免生成重复排列,而不是事后去重。

方法一:通过组合选择位置生成唯一排列

这种方法先统计每个元素的出现次数,然后通过选择每个元素要放置的位置来构建排列,从根本上杜绝重复:

from itertools import combinations

def unique_permutations(arr):
    # 先统计数组中每个元素的出现次数
    element_counts = {}
    for num in arr:
        element_counts[num] = element_counts.get(num, 0) + 1
    
    total_length = len(arr)
    all_indices = list(range(total_length))
    result = []
    
    def backtrack(positions_map, remaining_counts):
        # 所有元素都安排好位置了,构建排列并加入结果
        if not remaining_counts:
            permutation = [0] * total_length
            for num, indices in positions_map.items():
                for idx in indices:
                    permutation[idx] = num
            result.append(tuple(permutation))
            return
        
        # 取出当前要处理的元素和它的剩余次数
        current_num, current_count = next(iter(remaining_counts.items()))
        # 从剩余的位置里选current_count个位置放当前元素
        for selected_indices in combinations(all_indices, current_count):
            # 计算剩下的可用位置
            remaining_indices = [i for i in all_indices if i not in selected_indices]
            # 更新剩余计数和位置映射
            new_remaining = remaining_counts.copy()
            del new_remaining[current_num]
            new_positions = positions_map.copy()
            new_positions[current_num] = selected_indices
            # 递归处理下一个元素
            backtrack(new_positions, new_remaining)
    
    backtrack({}, element_counts)
    return result

# 测试你的数组
target_arr = [1, 1, 2, 3, 3, 3, 0, 0, 0, 0, 0]
unique_results = unique_permutations(target_arr)
print(f"实际唯一排列数: {len(unique_results)}")  # 输出27720,和理论值一致

方法二:递归+去重分支生成排列

这种方法用递归的方式构建排列,每次只选择当前剩余的不同元素,跳过重复的选择,减少无效递归分支:

from collections import Counter

def unique_permutations_recursive(arr):
    result = []
    element_counter = Counter(arr)
    
    def backtrack(current_perm):
        # 排列长度等于原数组长度时,加入结果
        if len(current_perm) == len(arr):
            result.append(tuple(current_perm))
            return
        # 遍历当前剩余的所有不同元素
        for num in element_counter:
            if element_counter[num] == 0:
                continue
            # 选择当前元素加入排列
            current_perm.append(num)
            element_counter[num] -= 1
            # 递归继续构建
            backtrack(current_perm)
            # 回溯,恢复计数
            current_perm.pop()
            element_counter[num] += 1
    
    backtrack([])
    return result

# 测试
target_arr = [1, 1, 2, 3, 3, 3, 0, 0, 0, 0, 0]
unique_results = unique_permutations_recursive(target_arr)
print(f"实际唯一排列数: {len(unique_results)}")

两种方法的优势对比

  • 方法一通过组合选择位置,计算量直接等于唯一排列数,没有任何冗余计算,效率最高,适合规模较大的数组。
  • 方法二的递归逻辑更直观,代码量更少,对于中小规模的数组足够高效。

总之,核心原则就是不要先生成所有排列再去重,这种做法在数组元素较多时,内存和时间都会直接爆炸,从根源上避免重复排列才是正确的思路。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:08:45