循環左移全排列生成算法

循環左移全排列生成算法是全排列生成算法的一種,與遞減進位全排列生成算法非常相似。所謂左循環搜尋法是指從起始數字開始向左搜尋,如果到了左邊界還沒有發現終止數字,則從右邊界開始繼續尋找,直到終止數字被發現。該算法的總算數複雜度為O(n*n*n!)。

算法原理

循環左移排列生成算法與遞減進位排列生成算法非常相似,同樣是在原始排列中找到比當前數字小的數字個數。只不過循環左移使用的是左循環搜尋法,而不是遞減進位法使用的右側搜尋。所謂左循環搜尋法是指從起始數字開始向左搜尋,如果到了左邊界還沒有發現終止數字,則從右邊界開始繼續尋找,直到終止數字被發現。

首先要確定循環左移排列到中介數的映射,就是要為每一個數字確定這樣一個區間。以9位數的排列為例,方法是以從9到2的順序依次產生中介數中的數字,中介數從右向左產生(即原排列的9產生的數字放到中介數右數第一位,8產生的數字放到中介數右數第二位,以此類推。這樣才能得到遞減進位制數)。對於9,產生的中介數字依然是9的右邊比9小的數字的個數。但是從8開始一直到2中的每一個數i,都是要做一個以i+1為開始數字,i為結束數字的左循環區間,並看這個左循環區間中比i小的數字的個數。依次類推,得到當前排列所對應的中介數。

對於從中介數到原排列的映射,從中介數的最右邊向左邊開始計數逐個計數。計數的方法是,對於中介數最右邊的數x9,從空格的最右端起數x9個空格,在第x9+1個空格上填上9。然後對於中介數的每一位xi,沿著左循環的方向數xi個未占用空格,在第xi+1個空格里填上i,即可完成還原。

算法步驟

設[a1,a2 ... aN],我們使用遞減進位制數表示中介數。從0到N!枚舉中介數,對於每箇中介數將其翻譯成對應的排列,對於中介數最右邊的數x9,從空格的最右端起數x9個空格,在第x9+1個空格上填上9。然後對於中介數的每一位xi,沿著左循環的方向數xi個未占用空格,在第xi+1個空格里填上i,最後剩餘的一個空位填上1,即可完成從中介數到原排列的轉化。

算法性能分析

記要全排列的元素總數為n,全排列一共n!種排列。我們在枚舉每一個中介數時,易知中介數有n-1位,對於中介數的每一位,我們需要掃描當前的n位數,找到當前位的位置,因此總複雜度為O(n*n*n!)。

效果展示

下表展示的是該算法下4!的排列的生成順序。

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

代碼實現

void rotate_left(int *a, int n) {

long len=count_factorial(n);

int dec_num[100] = {0};

bool vis[100];

for (int i = 0; i < len; i++) {

//求當前排列

memset(vis, 0, sizeof(vis));

a[n - 1 - dec_num[n - 1]] = n;

vis[n - 1 - dec_num[n - 1]] = 1;

int start = n - 1 - dec_num[n - 1];

for (int j = n - 2; j > 0 ; j--) {

int count = dec_num[j] + 1;

while (count){

start--;

if (start == -1) start = n - 1;

if (vis[start] == 0) count--;

}

a[start] = j + 1;

vis[start] = 1;

}

for (int j = n - 1; j >= 0; j--){

if (!vis[j]) {

vis[j] = 1;

a[j] = 1;

break;

}

}

//輸出排列

permutation_print(a,n);

//求下一中介數

dec_num[n - 1]++;

for (int i = n - 1; i > 0; i--) {

if (dec_num[i] == i + 1) {

dec_num[i - 1]++;

dec_num[i] = 0;

}

else break;

}

}

cout << "Total 8:" << len << endl;

}

相關詞條

熱門詞條

聯絡我們