是否有一种算法可以生成多重集的所有唯一循环排列?

Posted

技术标签:

【中文标题】是否有一种算法可以生成多重集的所有唯一循环排列?【英文标题】:Is there an algorithm to generate all unique circular permutations of a multiset? 【发布时间】:2011-03-28 22:19:29 【问题描述】:

我在做一些热心的编程时遇到了这个问题。问题可以表述如下:

对于多重集 A,令 P(A) 表示 A 的所有可能排列的集合。 P(A) 自然分为 等价的不相交子集 类,具有等价关系 “可以通过循环移位相关联”。枚举所有 这些等价类通过生成 他们每个人中只有一个成员。

例如,考虑多重集 0, 1, 1, 2。排列“0112”和“1201”是唯一排列,但后者可以通过循环移动前者找到,反之亦然。所需的算法不应同时生成两者。

当然,蛮力方法是可能的:只需使用任何多集排列算法生成排列(无论循环重复如何),并丢弃通过与先前结果比较发现的重复。然而,这在实践中往往效率低下。所需的算法应该需要最少的簿记,如果不是零的话。

非常感谢您对此问题的任何见解。

【问题讨论】:

【参考方案1】:

Sawada's algorithm

【讨论】:

感谢链接和对 Sawada 论文的引用。如果我有其他问题,我会花一些时间来研究它并回复。 好吧,我将其标记为解决方案 :) 我还发现了一篇针对密切相关问题的算法的论文,即从字母表中生成一定长度的所有项链。论文链接:dx.doi.org/10.1006/jagm.2000.1108【参考方案2】:

这种自下而上的做法稍微容易一些:

如果 A 只包含 1 个元素,则 P(A) 也包含一个排列。 如果 A 仅包含 2 个元素,则很容易看到相同的作品。

现在,假设您已经拥有包含 n 个元素的 A 的所有 P(A),并且您添加了一个元素。 它可以进入 P(A) 中任意排列中的任意 n 个点。

我认为这个想法可以直接转化为您选择的语言中的递归算法,希望我的解释足够清楚。

编辑:我知道我有点忽略了 A 可能包含重复元素的事实,但仍在考虑那部分:)

尽管如此 - 如果您在开始排列算法之前对 A 进行排序,我认为这可能会消除重复。 (还在想这个)

【讨论】:

【参考方案3】:

我在这里提出一个用python实现的解决方案

import itertools as it

L = ['a','b','c','d']
B = it.combinations(L,2)
swaplist = [e for e in B]
print 'List of elements to permute:' 
print swaplist
print
unique_necklaces = []
unique_necklaces.append(L)
for pair in swaplist:
    necklace = list(L)
    e1 = pair[0]
    e2 = pair[1]
    indexe1 = L.index(e1)
    indexe2 = L.index(e2)
    #swap
    necklace[indexe1],necklace[indexe2] = necklace[indexe2], necklace[indexe1]
    unique_necklaces.append(necklace)

for n in unique_necklaces:
    # Commented code display the rotation of the elements in each necklace
    print 'Necklaces'
    print n#, [n[-r:]+n[:-r]for r in (1,2,3)]   

这个想法是通过两个元素的排列来构建不同的项链。对于包含 a、b、c、d 四个元素的列表,算法得出:

['a', 'b', 'c', 'd']
['b', 'a', 'c', 'd']
['c', 'b', 'a', 'd']
['d', 'b', 'c', 'a']
['a', 'c', 'b', 'd']
['a', 'd', 'c', 'b']
['a', 'b', 'd', 'c']

【讨论】:

我不认为答案是正确的,因为应该只有 4!/4=6 案例,但你有 7 个案例!【参考方案4】:

为了直观地理解问题,我认为我们可以使用这个比喻。想象墙上有一个时钟,但它不是在面上有 12 个位置,而是有 n,其中 n 是你的集合中元素的数量。

那么每个等价类只是将A的一个元素分配到钟面上的一个位置。

一旦分配了来自同一个等价类的另一个排列,可以通过简单地旋转墙上的时钟来生成。

要生成 A 的另一个不相关排列,您需要让一个元素跳过至少一个其他元素。

现在我看到的算法将从分配开始,例如说我们在 A = a, b, c, d 中有四个元素,我们将它们分配到 12、3、6 和 9 位置分别为视觉清晰度。然后我们的第一个操作是交换 a 和 b。然后是 a 和 c,然后是 a 和 d,然后我们将转到 b 并将其与位于 3 位置的元素交换,现在是 c。

这样做直到我们达到 d 会从所有等价类中生成一个代表。

这不会处理重复,但它应该比生成 A 的所有排列更有效。

【讨论】:

感谢您的回复。实际上,这是我在这里发布问题之前的简要想法。出于某种原因,我忘记了它并使用了愚蠢的“生成所有排列”的东西:p 正如你所说,它仅在集合 A 是没有重复成员的“普通”集合时才有效,但它仍然是理解的一个很好的起点问题。【参考方案5】:

我想到的想法是,对于至少有一个元素只出现一次的任何集合,您可以将该元素放在所有答案列表的第一个位置,然后生成其余部分的所有排列号码。这是一个非常简单的解决方案,因为您的第一个元素是独一无二的,通过循环移动元素确保没有等效项。显然,您生成的所有解决方案都必须是唯一的。

明显的问题是,如果你没有单一的元素,那么这将完全崩溃。我把它放进去的主要原因是因为还有其他几个解决 no 重复的解决方案,我认为这个解决方案比它们更有效(解决更多案例),所以值得一提。就理解它的工作原理和实现它而言,它也非常简单。我只希望我的推理是合理的。 ;-)

编辑以获取更多想法:

我突然想到,这个原则可以推广到你有一定程度重复的情况。

如果您采用一个元素(我们现在假设它是重复的),您可以只查看它的排列以及哪些排列允许循环移位重复,就像之前假设您可以在不失一般性的情况下“锁定”一个元素。例如,如果您总共有 6 个元素并且 A 在此集合中出现两次,那么您可以:

AAXXXX、AXAXXX、AXXAXX、AXXXAX、AXXXXA

最后一个与第一个相同(在循环移位内),因此可以忽略,同上第二个和第四个。第三个(AXXAXX)可以由三个循环以返回到自身,因此具有循环的潜力。前两个永远不会产生循环,无论您循环多少次,因此您分配剩余的元素您只需要确保它们是唯一的分布,并且您可以保证得到唯一的循环结果。

对于可以循环的第三种模式 (AXXAXX),您需要查看元素 B 并为它们重复该过程。不幸的是,这一次您将无法使用锁定第一个值的技巧来节省时间。

我不能 100% 确定您将如何将其变成一个完全可运行的程序,但它是关于如何避免使用蛮力的一些想法。

【讨论】:

以上是关于是否有一种算法可以生成多重集的所有唯一循环排列?的主要内容,如果未能解决你的问题,请参考以下文章

计算包含某些项目的列表集的总唯一排列的算法

生成所有错位排列的算法

是否有一种快速算法可以将集合的所有分区生成为大小为 2 的子集(和一个大小为 1 的子集)?

生成具有重复元素的列表排列

是否有任何排列算法?

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