多重集是一个集合,其中所有元素可能不是唯一的。如何枚举集合元素之间所有可能的排列?
5 回答
生成所有可能的排列然后丢弃重复的排列是非常低效的。存在各种算法来直接生成按字典顺序或其他类型排序的多重集的排列。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!)
这是我将 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)
有用于多集排列生成的 O(1)(每个排列)算法,例如来自 Takaoka(带有实现)
来自文档:
>>> 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
您可以减少您的问题以枚举列表的所有排列。典型的排列生成算法采用一个列表并且不检查元素是否相等。所以你只需要从你的多重集中生成一个列表,并将它提供给你的排列生成算法。
例如,您有多重集 {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)