生成n个项目的k元组的算法,使得每个项目至少使用一次(k> n)

时间:2018-01-07 13:18:40

标签: algorithm combinations permutation combinatorics

给定一组n个项目,我想生成我们可以从这个集合中选择的所有方法(替换)k次,这样订单很重要,每个元素至少使用一次。所以k> = n有任何有效的安排。如果k = n,这只是一种排列。所以k> n有点像排列的扩展,但我不知道它叫什么。

当然很容易得到 算法,虽然速度非常慢:只需迭代所有可能的选择并抛弃那些至少没有每个元素的算法。因此,为了提高效率,需要类似于迭代排列的技巧,或者将其分解为可以直接使用现有排列算法的子问题。

我尝试通过使用python执行类似下面的操作,将其分解为排列和组合问题。

import itertools

def func(inputSet,k):
    n = len(inputSet)
    assert(k>n)
    # first, guarantee we have each element once
    for p in itertools.permutations(inputSet):
        # now select (k-n) locations to insert other elements
        for c in itertools.combinations_with_replacement(range(n+1),k-n):
            insertions = [c[i]+i for i in range(len(c))]
            out = list(p)
            for index in insertions:
                out.insert(index,0)
            # now select values to put in those locations
            for vals in itertools.product(inputSet,repeat=len(insertions)):
                for i in xrange(len(insertions)):
                    out[c[i]] = vals[i]
                yield tuple(out)

但是不同的插入可以产生相同的结果,因此第一次刺入可能不会从正确的路径开始。我可以添加条件来检查这些情况并过滤掉一些结果,但是用于求解过滤的组合迭代问题的算法可能不是最有效的算法。

  

这个“排列扩展”是否有名称?
  迭代这些安排的有效算法是什么?

3 个答案:

答案 0 :(得分:1)

对于它所称的内容,这与排列相同,只是略有不同。考虑一组元素P,你基本上要求生成与P的(k-n)个元素联合的集合P中的所有排列,可以用itertools.combinations_with_replacement找到。

要生成实际排列,您可以使用list(set(itertools.permutations))more_itertools.distinct_permutationshttps://more-itertools.readthedocs.io/en/latest/api.html#more_itertools.distinct_permutations

将其纳入实际代码

>>> x = [1,2,3]
>>> k = 5
>>> results = set()
>>> for y in itertools.combinations_with_replacement(x, k - len(x)):
...   for z in itertools.permutations(x + list(y)):
...     results.add(z)
...
>>> results
set([(1, 1, 1, 2, 3), (1, 3, 3, 1, 2), (1, 2, 3, 3, 2), (3, 3, 2, 1, 3), (1, 3, 3, 2, 2), (1, 1, 2, 2, 3), (3, 1, 2, 3, 2), (1, 1, 3, 2, 3), (1, 3, 1, 2, 2), (1, 2, 2, 1, 3), (3, 2, 1, 2, 2), (3, 1, 2, 1, 2), (3, 2, 1, 3, 1), (3, 1, 1, 3, 2), (2, 3, 3, 2, 1), (1, 2, 1, 3, 3), (3, 1, 2, 3, 3), (2, 3, 2, 1, 1), (2, 3, 2, 2, 1), (1, 2, 2, 3, 3), (2, 1, 3, 2, 3), (2, 2, 2, 3, 1), (1, 3, 2, 2, 3), (2, 3, 3, 1, 1), (1, 2, 1, 1, 3), (3, 2, 2, 1, 1), (2, 1, 2, 2, 3), (2, 2, 1, 3, 1), (1, 3, 3, 2, 3), (2, 3, 3, 1, 3), (3, 2, 3, 2, 1), (3, 2, 3, 1, 1), (2, 1, 1, 2, 3), (3, 3, 1, 1, 2), (3, 2, 2, 2, 1), (2, 2, 3, 2, 1), (1, 3, 1, 2, 3), (2, 2, 3, 1, 2), (3, 1, 2, 1, 3), (2, 1, 1, 3, 3), (3, 3, 2, 2, 1), (1, 1, 2, 3, 1), (1, 2, 2, 3, 2), (3, 2, 1, 1, 2), (2, 1, 3, 2, 2), (1, 3, 2, 2, 2), (3, 1, 1, 1, 2), (3, 3, 2, 1, 1), (2, 3, 3, 1, 2), (3, 2, 1, 3, 2), (1, 2, 1, 3, 1), (2, 3, 1, 2, 3), (1, 2, 3, 2, 1), (3, 1, 3, 1, 2), (3, 3, 1, 2, 2), (1, 2, 3, 1, 1), (2, 2, 1, 1, 3), (2, 1, 1, 3, 2), (1, 1, 2, 3, 2), (3, 2, 1, 1, 3), (2, 1, 3, 2, 1), (2, 1, 1, 3, 1), (1, 3, 2, 2, 1), (1, 3, 2, 1, 1), (3, 2, 2, 1, 3), (2, 2, 3, 3, 1), (3, 1, 1, 2, 1), (2, 2, 1, 3, 3), (1, 3, 3, 2, 1), (3, 2, 3, 1, 2), (3, 1, 2, 3, 1), (2, 2, 2, 1, 3), (1, 1, 3, 1, 2), (1, 1, 2, 1, 3), (2, 1, 3, 3, 2), (3, 3, 1, 2, 3), (1, 3, 2, 3, 2), (3, 3, 2, 3, 1), (3, 1, 3, 2, 2), (2, 1, 3, 1, 1), (1, 1, 2, 3, 3), (2, 1, 3, 1, 2), (1, 3, 2, 1, 2), (1, 2, 1, 2, 3), (3, 2, 2, 1, 2), (3, 1, 1, 2, 2), (2, 2, 1, 3, 2), (2, 3, 2, 3, 1), (1, 1, 1, 3, 2), (2, 3, 1, 2, 1), (1, 2, 3, 1, 3), (2, 3, 1, 1, 1), (1, 2, 3, 2, 3), (2, 1, 2, 3, 3), (3, 2, 1, 2, 3), (1, 2, 2, 2, 3), (3, 2, 1, 2, 1), (2, 1, 1, 1, 3), (1, 3, 2, 3, 3), (1, 1, 3, 3, 2), (3, 2, 2, 3, 1), (3, 1, 3, 2, 3), (2, 1, 2, 1, 3), (1, 3, 3, 3, 2), (3, 2, 3, 3, 1), (2, 2, 3, 1, 3), (3, 2, 1, 1, 1), (2, 1, 3, 1, 3), (1, 2, 1, 3, 2), (3, 3, 1, 3, 2), (1, 3, 2, 1, 3), (2, 3, 1, 3, 2), (3, 1, 1, 2, 3), (2, 3, 3, 3, 1), (1, 1, 3, 2, 1), (2, 3, 1, 1, 2), (1, 2, 3, 2, 2), (2, 1, 2, 3, 2), (1, 3, 1, 1, 2), (3, 1, 3, 3, 2), (3, 1, 2, 2, 2), (3, 3, 1, 2, 1), (3, 3, 3, 1, 2), (3, 2, 3, 1, 3), (1, 3, 1, 3, 2), (2, 3, 2, 1, 3), (2, 1, 3, 3, 3), (1, 2, 2, 3, 1), (2, 3, 1, 3, 3), (1, 2, 3, 3, 1), (2, 3, 1, 2, 2), (3, 3, 2, 1, 2), (2, 3, 1, 3, 1), (3, 2, 1, 3, 3), (1, 1, 3, 2, 2), (2, 3, 1, 1, 3), (2, 1, 2, 3, 1), (1, 2, 3, 3, 3), (1, 3, 1, 2, 1), (3, 1, 2, 2, 3), (3, 1, 2, 2, 1), (2, 1, 3, 3, 1), (3, 1, 2, 1, 1), (1, 3, 2, 3, 1), (1, 2, 3, 1, 2), (3, 1, 3, 2, 1), (2, 2, 1, 2, 3), (2, 3, 2, 1, 2), (3, 3, 3, 2, 1), (2, 2, 3, 1, 1)])

请注意,这会非常快速地组合爆炸,但是因为itertools.combinations_with_replacementitertools.permutations都会返回生成器,所以您还可以yield结果。你也可以递归地自己写,但我个人认为这不太令人满意。

我相信在这里使用distinct_permutations也足够了,你最终得到一个完全不同的结果列表,因为外部循环的每次迭代都会产生不同的频率签名元素。

答案 1 :(得分:0)

这是一个额外的解决方案,它也以字母顺序迭代。

import itertools

def _recurse_extended_perm(remaining,inputList,requireList):
    if remaining==len(requireList):
        for p in itertools.permutations(requireList):
            yield p
    elif remaining==1:
        for x in inputList:
            yield [x]
    else:
        for x in inputList:
            req = list(requireList)
            if x in req:
                req.remove(x)
            for seq in _recurse_extended_perm(remaining-1,inputList,req):
                out = [x]
                out.extend(seq)
                yield out

def extended_permutation(seq,k):
    inputList = list(seq)
    inputList.sort()
    assert(k>len(inputList))
    for seq in _recurse_extended_perm(k,inputList,inputList):
        yield tuple(seq)

答案 2 :(得分:0)

我们可以递归地生成列表。这是一个无模块实现,也为我们提供了一点灵活性:

def multisets_with_at_least_m(inputList, k, m):
  def f(idx, n, multiset):
    if idx == len(inputList):
      return [multiset]

    if (len(inputList) - idx) * m > n:
      return []

    minNum = n if idx == len(inputList) - 1 else m
    maxNum = n - (len(inputList) - idx) * m + m

    results = []

    for i in xrange(minNum, maxNum + 1):
      multisetCopy = multiset.copy()
      multisetCopy[inputList[idx]] = i
      results = results + f(idx + 1, n - i, multisetCopy) 

    return results

  return f(0, k, {})

def distinct_permutations_from_multiset(multiset):
  def f(multiset, permutation):
    if multiset == {}:
      return [permutation]

    results = []

    for k in multiset:
      multisetCopy = multiset.copy()
      if multiset[k] == 1:
        del multisetCopy[k]
      else:
        multisetCopy[k] = multisetCopy[k] - 1
      results = results + f(multisetCopy, permutation + [k])

    return results

  return f(multiset, [])

输出:

print [distinct_permutations_from_multiset(x) for x in multisets_with_at_least_m([1,2,3], 5, 1)]

"""
 [[[1, 2, 3, 3, 3], [1, 3, 2, 3, 3], [1, 3, 3, 2, 3], [1, 3, 3, 3, 2]
 , [2, 1, 3, 3, 3], [2, 3, 1, 3, 3], [2, 3, 3, 1, 3], [2, 3, 3, 3, 1]
 , [3, 1, 2, 3, 3], [3, 1, 3, 2, 3], [3, 1, 3, 3, 2], [3, 2, 1, 3, 3]
 , [3, 2, 3, 1, 3], [3, 2, 3, 3, 1], [3, 3, 1, 2, 3], [3, 3, 1, 3, 2]
 , [3, 3, 2, 1, 3], [3, 3, 2, 3, 1], [3, 3, 3, 1, 2], [3, 3, 3, 2, 1]]
, [[1, 2, 2, 3, 3], [1, 2, 3, 2, 3], [1, 2, 3, 3, 2], [1, 3, 2, 2, 3]
 , [1, 3, 2, 3, 2], [1, 3, 3, 2, 2], [2, 1, 2, 3, 3], [2, 1, 3, 2, 3]
 , [2, 1, 3, 3, 2], [2, 2, 1, 3, 3], [2, 2, 3, 1, 3], [2, 2, 3, 3, 1]
 , [2, 3, 1, 2, 3], [2, 3, 1, 3, 2], [2, 3, 2, 1, 3], [2, 3, 2, 3, 1]
 , [2, 3, 3, 1, 2], [2, 3, 3, 2, 1], [3, 1, 2, 2, 3], [3, 1, 2, 3, 2]
 , [3, 1, 3, 2, 2], [3, 2, 1, 2, 3], [3, 2, 1, 3, 2], [3, 2, 2, 1, 3]
 , [3, 2, 2, 3, 1], [3, 2, 3, 1, 2], [3, 2, 3, 2, 1], [3, 3, 1, 2, 2]
 , [3, 3, 2, 1, 2], [3, 3, 2, 2, 1]]
, [[1, 2, 2, 2, 3], [1, 2, 2, 3, 2], [1, 2, 3, 2, 2], [1, 3, 2, 2, 2]
 , [2, 1, 2, 2, 3], [2, 1, 2, 3, 2], [2, 1, 3, 2, 2], [2, 2, 1, 2, 3]
 , [2, 2, 1, 3, 2], [2, 2, 2, 1, 3], [2, 2, 2, 3, 1], [2, 2, 3, 1, 2]
 , [2, 2, 3, 2, 1], [2, 3, 1, 2, 2], [2, 3, 2, 1, 2], [2, 3, 2, 2, 1]
 , [3, 1, 2, 2, 2], [3, 2, 1, 2, 2], [3, 2, 2, 1, 2], [3, 2, 2, 2, 1]]
, [[1, 1, 2, 3, 3], [1, 1, 3, 2, 3], [1, 1, 3, 3, 2], [1, 2, 1, 3, 3]
 , [1, 2, 3, 1, 3], [1, 2, 3, 3, 1], [1, 3, 1, 2, 3], [1, 3, 1, 3, 2]
 , [1, 3, 2, 1, 3], [1, 3, 2, 3, 1], [1, 3, 3, 1, 2], [1, 3, 3, 2, 1]
 , [2, 1, 1, 3, 3], [2, 1, 3, 1, 3], [2, 1, 3, 3, 1], [2, 3, 1, 1, 3]
 , [2, 3, 1, 3, 1], [2, 3, 3, 1, 1], [3, 1, 1, 2, 3], [3, 1, 1, 3, 2]
 , [3, 1, 2, 1, 3], [3, 1, 2, 3, 1], [3, 1, 3, 1, 2], [3, 1, 3, 2, 1]
 , [3, 2, 1, 1, 3], [3, 2, 1, 3, 1], [3, 2, 3, 1, 1], [3, 3, 1, 1, 2]
 , [3, 3, 1, 2, 1], [3, 3, 2, 1, 1]]
, [[1, 1, 2, 2, 3], [1, 1, 2, 3, 2], [1, 1, 3, 2, 2], [1, 2, 1, 2, 3]
 , [1, 2, 1, 3, 2], [1, 2, 2, 1, 3], [1, 2, 2, 3, 1], [1, 2, 3, 1, 2]
 , [1, 2, 3, 2, 1], [1, 3, 1, 2, 2], [1, 3, 2, 1, 2], [1, 3, 2, 2, 1]
 , [2, 1, 1, 2, 3], [2, 1, 1, 3, 2], [2, 1, 2, 1, 3], [2, 1, 2, 3, 1]
 , [2, 1, 3, 1, 2], [2, 1, 3, 2, 1], [2, 2, 1, 1, 3], [2, 2, 1, 3, 1]
 , [2, 2, 3, 1, 1], [2, 3, 1, 1, 2], [2, 3, 1, 2, 1], [2, 3, 2, 1, 1]
 , [3, 1, 1, 2, 2], [3, 1, 2, 1, 2], [3, 1, 2, 2, 1], [3, 2, 1, 1, 2]
 , [3, 2, 1, 2, 1], [3, 2, 2, 1, 1]]
, [[1, 1, 1, 2, 3], [1, 1, 1, 3, 2], [1, 1, 2, 1, 3], [1, 1, 2, 3, 1]
 , [1, 1, 3, 1, 2], [1, 1, 3, 2, 1], [1, 2, 1, 1, 3], [1, 2, 1, 3, 1]
 , [1, 2, 3, 1, 1], [1, 3, 1, 1, 2], [1, 3, 1, 2, 1], [1, 3, 2, 1, 1]
 , [2, 1, 1, 1, 3], [2, 1, 1, 3, 1], [2, 1, 3, 1, 1], [2, 3, 1, 1, 1]
 , [3, 1, 1, 1, 2], [3, 1, 1, 2, 1], [3, 1, 2, 1, 1], [3, 2, 1, 1, 1]]]
"""