【问题标题】:Python permutations for array of elements with specific repetitions [duplicate]具有特定重复的元素数组的Python排列[重复]
【发布时间】:2019-01-18 19:49:00
【问题描述】:

如何以最快的方式计算元素数组的排列,每个元素在排列中重复一定次数?

例如:

elements = [0, 1]
repetitions = [2, 3]

我希望它返回 10 个独特的排列:

[(0, 0, 1, 1, 1), (0, 1, 0, 1, 1), (1, 0, 1, 0, 1) ....]

【问题讨论】:

  • 你的元素向量中只有 0 和 1 还是你想要任何随机整数?
  • 这些被称为多重集,可以用multiset_permutations sympy生成。

标签: python combinations permutation


【解决方案1】:

这是一个解决方案:我们的想法是找到可以放置第一个元素的所有可能索引,然后递归地找到剩余元素的可能索引。这样,我们就可以保证直接生成唯一的输出。

from itertools import combinations


def combs(elements, repetitions, index=0, indices_left=None, already_set=None):
    if indices_left is None:
        already_set = [None] * sum(repetitions)
        indices_left = set(range(sum(repetitions)))

    element = elements[index]
    number = repetitions[index]

    for indices_choice in combinations(indices_left, number):
        currently_set = already_set[:]
        for i in indices_choice:
            currently_set[i] = element
        remaining_indices = indices_left - set(indices_choice)
        if not remaining_indices:
            yield currently_set
        else:
            yield from combs(elements, repetitions, index+1, remaining_indices, currently_set)

根据您的示例输入,这给了我们:

elements = [0, 1]
repetitions = [2, 3]

for comb in combs(elements, repetitions):
    print(comb)

# [0, 0, 1, 1, 1]
# [0, 1, 0, 1, 1]
# [0, 1, 1, 0, 1]
# [0, 1, 1, 1, 0]
# [1, 0, 0, 1, 1]
# [1, 0, 1, 0, 1]
# [1, 0, 1, 1, 0]
# [1, 1, 0, 0, 1]
# [1, 1, 0, 1, 0]
# [1, 1, 1, 0, 0]

另一个元素更多的例子:

elements = [0, 1, 2]
repetitions = [2, 3, 2]

c = combs(elements, repetitions)

print(next(c))
# [0, 0, 1, 1, 1, 2, 2]

print(next(c))
# [0, 0, 1, 1, 2, 1, 2]

print(len(list(combs(elements, repetitions))))
# 210

一点速度测试:

elements = [0, 1]
repetitions = [10, 10]

a = list(combs(elements, repetitions))
print(len(a))
# 184756

%timeit list(combs(elements, repetitions))
# 1.15 s ± 5.52 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

【讨论】:

    【解决方案2】:

    类似于@Erwan,它也可以工作:

    import itertools as it
    elements = [0, 1]
    repetitions = [2, 3]
    arr = []
    for j,i in enumerate(repetitions):
       arr = arr +[elements[j]]*i
    
    set(list(it.permutations(arr)))
    

    【讨论】:

    • 这段代码产生 120 个元组,而只有 10 个不同的输出。
    • @ThierryLathuille 已更正。谢谢。
    • 但问题是,在您开始使用set() 删除重复项之前,您的代码需要生成大量相同的排列,全部存储在内存中。这很快就会变得不切实际:例如,对于repetitions = [10, 10],您需要生成一个包含 2432902008176640000 (2.4e18) 项的列表,由于需要时间和空间,这是不可能的,而只有 184756 个唯一排列。
    【解决方案3】:

    您可以使用来自itertools 模块的permutations

    >>> from itertools import permutations
    >>> list(permutations([0]*2 + [1]*3, 5))
    [(0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0)]
    

    更自动的版本:

    >>> e = [0, 1]
    >>> r = [2, 3]
    >>> b = sum([[i]*j for i, j in zip(e, r)], [])
    >>> list(permutations(b, len(b)))
    [(0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0)]
    

    编辑:如@JosephWood 所述如果您不想重复(00111 可能出现多次),请查看here

    Edit2:如果您的元素中只有 0,1 并且您不想重复,则可以使用像 (j for j in ([i%2, i//2%2, i//4%2, i//8%2, i//16%2] for i in range(0, 2**5)) if sum(j)==3) 这样的二进制表示。不知道是不是更快。

    【讨论】:

    • 很好,也许我们可以找到元素的解决方案
    【解决方案4】:

    这不是一个完整的答案,而是一个总体思路。 我几乎可以肯定,没有递归和组合(itertools)就没有简单的方法可以做到这一点。 假设您已经定义了数据、元素和重复。

    • n 是元素的大小
    • s 是所有重复项的总和, 所以它是每个排列的最终元素数 正在寻找。

    我猜你必须进行递归,这将转化为递归函数。 En 是元素的数组。 Rn 是重复数组。 所以 En-1 是 n-1 个第一个元素和 Rn-1 个第一个元素的数组。 如果您知道 En-1 和 Rn-1 的解决方案,您只需计算与 itertools 包的组合,see here。 以您的示例为例,您知道 E2 和 R2 对吗? 我会找时间写代码,不知道你对递归有多熟悉,可能很难理解。

    from itertools import combinations
    
    def multipleElementsPermutations(elements, repetitions): #elements is En, repetitions is Rn
        if len(elements) != len(repetitions) or len(elements) <2 or len(repetitions) <2:
            return None 
        elif len(elements) == 2:
            #Write some code using itertools to solve the case n=2
            return list(permutations(elements[0]*repetitions[0] + elements[1]*repetitions[1], repetitions[0]+repetitions[1]))
        else:
            last_element = elements[-1]
            last_repetition = repetitions[-1]
            #Here thus function will call itself with the n-1 arguments
            previous_case_solution = multipleElementsPermutation(
                elements[:-1], #En-1
                repetitions[:-1] #Rn-1
            )
            #Write some code here to find different ways to add last element
    

    很抱歉没有时间编写完整的解决方案。 我猜这更像是一个数学问题而不是代码问题。

    使用下面 Erwan 的想法,也许这段代码可以工作:

    from itertools import permutations
    
    n = len(elements)
    repeated_elements_array = [element[i]*repetition[i] for i in range(n)]
    repeated_elements_list = []
    for repeated_elements in repeated_elements_array:
        repeated_elements_list += repeated_elements
    s = 0
    for repetition in repetitions:
        s += repetition
    solution = permutations(repeated_elements_list, s)
    

    【讨论】:

      猜你喜欢
      • 2017-03-10
      • 2014-02-17
      • 2020-11-07
      • 1970-01-01
      • 2011-05-14
      • 1970-01-01
      • 2023-04-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多