2012-01-27 53 views
5

生成不包括循环转动

所有排列所以我需要一个算法来产生不包括循环转动号码列表的所有排列(例如,[1,2,3] == [2,3,1] == [3,1,2])。

当序列中至少有一个唯一编号时,它非常直接,取出该唯一编号,生成其余编号的所有排列(但对“标准”排列算法进行小修改)并添加前面的唯一号码。

为了生成我发现,这是必要的置换代码更改为排列:

def permutations(done, options) 
    permuts = [] 
    seen = [] 
    for each o in options 
     if o not in seen 
      seen.add(o) 
      permuts += permutations(done+o, options.remove(o)) 
    return permuts 

使用只有在选择每一个独特的编号,一旦意味着你没有得到322的两倍。

当没有唯一元素时,该算法仍然输出旋转,例如,对于[1,1,2,2]它会输出[1,1,2,2],[1,2,2,1]和[1,2,1,2],前两个是循环旋转。

那么有没有一种有效的算法,可以让我产生所有的排列而不必事后去除循环旋转?

如果不是,那么删除循环旋转的最有效方法是什么?

注意:这是而不是使用Python,而是C++。

+0

这不是一个[生成多重集的所有独特循环排列]的副本(http://stackoverflow.com/questions/3467914/is-there-an-algorithm-to-generate-all-unique-circular -permutations-的-A-多重集)?那里有一些很好的答案。 – Kalin 2014-05-23 02:42:14

回答

5

考虑测试每个你输出的排列,寻找一个周期性的旋转,比你所得到的“词法”早。如果有的话,不要退回 - 它会在其他地方被列举出来。

选择“唯一”第一个元素(如果存在)可帮助您优化。你知道如果你修复了第一个元素,并且它是唯一的,那么你不可能通过旋转来重复它。另一方面,如果没有这种独特的元素,只需选择发生最少的元素。这样你只需要检查有第一个元素的循环旋转。 (例如,当您生成[1,2,2,1]时 - 您只需检查[1,1,2,2],而不是[2,2,1,1]或[2,1,1,2 ])。


OK,伪...显然为O(n!),我相信没有更聪明的办法,因为案件 “的所有符号不同” 显然有返回(N-1)!元素。

// generate all permutations with count[0] 0's, count[1] 1's... 
def permutations(count[]) 
    if(count[] all zero) 
     return just the empty permutation; 
    else 
     perms = []; 
     for all i with count[i] not zero 
      r = permutations(copy of count[] with element i decreased); 
      perms += i prefixed on every element of r 
     return perms; 

// generate all noncyclic permutations with count[0] 0's, count[1] 1's... 
def noncyclic(count[]) 
    choose f to be the index with smallest count[f]; 
    perms = permutations(copy of count[] with element f decreased); 
    if (count[f] is 1) 
     return perms; 
    else 
     noncyclic = []; 
     for each perm in perms 
      val = perm as a value in base(count.length); 
      for each occurence of f in perm 
       test = perm rotated so that f is first 
       tval = test as a value in base(count.length); 
       if (tval < val) continue to next perm; 
      if not skipped add perm to noncyclic; 
     return noncyclic; 

// return all noncyclic perms of the given symbols 
def main(symbols[]) 
    dictionary = array of all distinct items in symbols; 
    count = array of counts, count[i] = count of dictionary[i] in symbols 
    nc = noncyclic(count); 
    return (elements of nc translated back to symbols with the dictionary) 
+0

效率会不会更高至少在内存方面)可以检查它是否是置换函数中的'最小'旋转而不是noncyclc,因此您不必在存储中存储太多内容,或者增益几乎可以忽略不计? – AdrianG 2012-01-27 07:19:44

+0

你必须通过递归传递状态......并且能够进行测试,比如“自从我的第一个f后面跟着一个x,确保我添加的任何其他f后面跟着x或更大” 。看起来非常艰难。 – 2012-01-27 07:31:56

+0

我不确定你的意思是通过传递状态,当我编写了一个快速测试并且有一个小函数发现了'最小'旋转并将其与当前置换 – AdrianG 2012-01-28 06:56:54

1

该解决方案将涉及itertools.permutations的使用,set()和一些很好的老式集差异。请记住,查找排列的运行时间仍然是O(n!)。我的解决方案也不会在线,但可能是一个更优雅的解决方案,允许您这样做(并且不涉及itertools.permutations)。为此,这是完成任务的直接方式。

步骤1:使用给定的第一个元素生成循环的算法。对于列表[1, 1, 2, 2],这将给我们[1, 1, 2, 2], [1, 2, 2, 1], [2, 1, 1, 2], [2, 2, 1, 1]

def rotations(li): 
    count = 0 
    while count < len(li): 
     yield tuple(li) 
     li = li[1:] + [li[0]] 
     count += 1 

第2步:导入itertools.permutations给我们摆在首位的排列,那么其结果设置为set

from itertools import permutations 
perm = set(permutations([1, 1, 2, 2])) 

第3步:使用生成器给我们自己的集合,与循环(我们想摆脱自己的东西)。

cycles = set(((i for i in rotations([1, 1, 2, 2])))) 

第4步:对每个应用设置差异,并删除周期。

perm = perm.difference(cycles) 

希望这会帮助你。我愿意接受建议和/或更正。

+0

嗯,当我运行代码而不是(1,1,2,2)和()时,似乎返回'set([(1,2,1,2),(2,1,2,1)]) 1,2,1,2)我也更喜欢没有绑定到python的东西,因为我实际上是用C++编写的。 – AdrianG 2012-01-27 03:28:07

+0

不知道为什么包含Python标记。说实话,有点误导。可以进行修改以提供所需的输出,但这或多或少是一块垫脚石,或者可以从头开始。 – Makoto 2012-01-27 03:31:49

+0

好吧,是的,看到我在原始问题上添加了关于python标签的注释(我没有添加它,但删除它破坏了所有突出显示,不知道是否有解决方法) – AdrianG 2012-01-27 03:38:58

3

对于所有数字都不同的置换情况,这很简单。假设这些数字是1,2,...,n,然后生成1,2,...,n-1的所有排列,并在前面粘贴n。这给出了全集模循环旋转的所有排列。例如,对于n=4,你会做

4 1 2 3 
4 1 3 2 
4 2 1 3 
4 2 3 1 
4 3 1 2 
4 3 2 1 

这可以确保1,2,3,4每个排列的一些循环旋转出现在列表中只出现一次。

对于一般情况下,您想要多重集的排列(允许重复条目),您可以使用类似的技巧。删除最大字母n的所有实例(类似于忽略上述示例中的4)并生成剩余多重集的所有排列。下一步是将n以规范方式放回到排列中(类似于在上例中将4放在开头)。

这真的只是找到所有Lyndon words的情况下产生necklaces

+0

进行比较时,我刚传递了'锚'感谢链接 – 2012-01-28 22:13:26

+0

不知道他们被称为项链,这可能会使研究更容易,谢谢你。 – AdrianG 2012-01-29 00:23:45

+0

这解决了我的问题,谢谢! – Tommy 2016-06-10 02:25:50

1

首先我会告诉容器和算法,我们会using

#include <vector> 
#include <set> 
#include <algorithm> 
#include <iostream> 
#include <iterator> 

using std::vector; 
using std::set; 
using std::sort; 
using std::next_permutation; 
using std::copy; 
using std::ostream_iterator; 
using std::cout; 

接下来我们vector这将代表Permutation

typedef vector<unsigned int> Permutation; 

我们需要一个比较对象来检查是否有错误呃置换是一个旋转:

struct CompareCyclicPermutationsEqual 
{ 
    bool operator()(const Permutation& lhs, const Permutation& rhs); 
}; 

而且typedef它采用循环比较对象set

typedef set<Permutation, CompareCyclicPermutationsEqual> Permutations; 

然后主要功能是相当简单:

int main() 
{ 
    Permutation permutation = {1, 2, 1, 2}; 
    sort(permutation.begin(). permutation.end()); 

    Permutations permutations; 

    do { 
     permutations.insert(permutation); 
    } while(next_permutation(numbers.begin(), numbers.end())) 


    copy(permutations.begin(), permutations.end(), 
     ostream_iterator<Permutation>(cout, "\n"); 

    return 0; 
} 

输出:

1, 1, 2, 2, 
1, 2, 1, 2, 

我还没有实施CompareCyclicPermutationsEqual呢。你也需要实现ostream& operator<<(ostream& os, const Permutation& permutation)

+0

我喜欢这是多么简单,但不是有点慢?我不知道stl集是如何工作的,但我认为这是一个自我平衡的BST,所以不会是沿着O(L^2 log n)行插入到集合中的东西?或者你可以使用不同的比较方法(我认为我碰到了一个用于旋转比较的O(n)算法但是找不到它)可以把它归结为O(L log n)? – AdrianG 2012-01-28 02:45:37

+0

它是这个页面上最快的C++实现(c: – 2012-01-28 11:24:05

+0

我认为在该集合的末尾插入会让事情变得更好,今晚会有这样的想法 – 2012-01-28 11:25:08