【问题标题】:Algorithm generating k-tuples of n items such that each is used at least once (k>n)生成 n 个项目的 k 元组的算法,使得每个项目至少使用一次 (k>n)
【发布时间】:2018-06-16 16:12:31
【问题描述】:

给定一组 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)

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

这个“置换扩展”有名字吗?
什么是迭代排列的有效算法?

【问题讨论】:

  • 终于完成了我的无模块代码:)(它可以控制“至少”使用了多少项目。)

标签: algorithm combinations permutation combinatorics


【解决方案1】:

就其名称而言,这与排列相同,只是框架略有不同。考虑元素 P 的集合,您实际上是在要求生成集合 P 中与 P 的 (k-n) 个元素联合的所有排列,可以通过 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 也足够了,您最终会得到一个完全不同的结果列表,因为外部循环的每次迭代都会导致元素的频率签名不同。

【讨论】:

  • 这是一种非常好的看待它的方式。使用 distinct_permutations 效果很好。
【解决方案2】:

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

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)

【讨论】:

    【解决方案3】:

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

    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]]]
    """
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-16
      • 1970-01-01
      相关资源
      最近更新 更多