2012-01-27 75 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