【问题标题】: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”是唯一排列,但后者可以通过循环移动前者找到,反之亦然。所需的算法不应同时生成两者。

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

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

【问题讨论】:

    标签: algorithm permutation multiset necklaces


    【解决方案1】:

    【讨论】:

    • 感谢链接和对 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% 确定您将如何将其变成一个完全可运行的程序,但它是关于如何避免使用蛮力的一些想法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-03-14
        • 2020-02-07
        • 2020-03-11
        • 1970-01-01
        • 1970-01-01
        • 2019-10-10
        • 2022-12-04
        • 2013-11-09
        相关资源
        最近更新 更多