如何生成多重集的所有排列?

Posted

技术标签:

【中文标题】如何生成多重集的所有排列?【英文标题】:How to generate all the permutations of a multiset? 【发布时间】:2013-11-09 15:56:04 【问题描述】:

多重集是一个集合,其中所有元素可能不是唯一的。如何枚举集合元素之间所有可能的排列?

【问题讨论】:

我假设您知道如何为唯一集合生成排列。对于多集合,对集合进行排序,只改变一个逻辑,如果与当前数字相同,则丢弃集合中下一个数字的排列。 【参考方案1】:

生成所有可能的排列然后丢弃重复的排列是非常低效的。存在各种算法来直接生成按字典顺序或其他类型排序的多重集的排列。 Takaoka 的算法就是一个很好的例子,但可能 Aaron Williams 的算法更好

http://webhome.csc.uvic.ca/~haron/CoolMulti.pdf

此外,它已在 R 包“multicool”中实现。

顺便说一句,如果您只想要不同排列的总数,答案是多项式系数: 例如,如果你有 n_a 个元素 'a'、n_b 个元素 'b'、n_c 个元素 'c', 不同排列的总数为 (n_a+n_b+n_c)!/(n_a!n_b!n_c!)

【讨论】:

链接现在已断开。具有多种语言实现的算法现在位于this link。【参考方案2】:

这是我将 Takaoka 多集排列算法翻译成 Python(可在此处和 repl.it 获得):

def msp(items):
  '''Yield the permutations of `items` where items is either a list
  of integers representing the actual items or a list of hashable items.
  The output are the unique permutations of the items given as a list
  of integers 0, ..., n-1 that represent the n unique elements in
  `items`.

  Examples
  ========

  >>> for i in msp('xoxox'):
  ...   print(i)

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

  Reference: "An O(1) Time Algorithm for Generating Multiset Permutations", Tadao Takaoka
  https://pdfs.semanticscholar.org/83b2/6f222e8648a7a0599309a40af21837a0264b.pdf
  '''

  def visit(head):
      (rv, j) = ([], head)
      for i in range(N):
          (dat, j) = E[j]
          rv.append(dat)
      return rv

  u = list(set(items))
  E = list(reversed(sorted([u.index(i) for i in items])))
  N = len(E)
  # put E into linked-list format
  (val, nxt) = (0, 1)
  for i in range(N):
      E[i] = [E[i], i + 1]
  E[-1][nxt] = None
  head = 0
  afteri = N - 1
  i = afteri - 1
  yield visit(head)
  while E[afteri][nxt] is not None or E[afteri][val] < E[head][val]:
      j = E[afteri][nxt]  # added to algorithm for clarity
      if j is not None and E[i][val] >= E[j][val]:
          beforek = afteri
      else:
          beforek = i
      k = E[beforek][nxt]
      E[beforek][nxt] = E[k][nxt]
      E[k][nxt] = head
      if E[k][val] < E[head][val]:
          i = k
      afteri = E[i][nxt]
      head = k
      yield visit(head)

【讨论】:

【参考方案3】:

多重集排列生成有 O(1)(每排列)算法,例如,from Takaoka (with implementation)

【讨论】:

【参考方案4】:

sympy 提供multiset_permutations

来自文档:

>>> from sympy.utilities.iterables import multiset_permutations
>>> from sympy import factorial
>>> [''.join(i) for i in multiset_permutations('aab')]
['aab', 'aba', 'baa']
>>> factorial(len('banana'))
720
>>> sum(1 for _ in multiset_permutations('banana'))
60

【讨论】:

【参考方案5】:

您可以将问题简化为枚举列表的所有排列。典型的排列生成算法采用一个列表并且不检查元素是否相等。所以你只需要从你的多重集中生成一个列表,并将它提供给你的排列生成算法。

例如,您有多重集 1,2,2。

您将其转换为列表 [1,2,2]。

并生成所有排列,例如在python中:

import itertools as it
for i in it.permutations([1,2,2]):
   print i

你会得到输出

(1, 2, 2)
(1, 2, 2)
(2, 1, 2)
(2, 2, 1)
(2, 1, 2)
(2, 2, 1)

问题是,您会反复得到一些排列。一个简单的解决方案就是将它们过滤掉:

import itertools as it
permset=set([i for i in it.permutations([1,2,2])])
for x in permset:
   print x

输出:

(1, 2, 2)
(2, 2, 1)
(2, 1, 2)

【讨论】:

这里的排列不断重复。

以上是关于如何生成多重集的所有排列?的主要内容,如果未能解决你的问题,请参考以下文章

hdu5651 xiaoxin juju needs help (多重集的全排列+逆元)

组合数学常用公式总结-更新中

nth Permutation LightOJ - 1060

生成可重集的排列

如何生成列表的所有排列?

Facebook按钮我正在使用firebaseUI多重登录,除了Facebook Builder().build()外,所有按钮都在生成,如何使该按钮膨胀?