鄰位對換法

鄰位對換法是全排列生成算法中的其中一種,它的換位是雙向的,通過保存數字的“方向性”來快速得到下一個排列。

設我們要生成n=9的全排列(1,2,3,4,5,6,7,8,9),設定b2b3b4b5b6b7b8b9為我們要求的中介數。

2的方向一定是向左。b2就是從2開始,背向2的方向所有比2小的數字的個數。知道了b2的值之後就可以推 導出b3b4……直到b9的值。

規則如下:

對於每一個大於2的數字i,

如果i為奇數,其方向性決定於bi-1的奇偶性,奇向右、偶向左。

如果i為偶數,其方向性決定於bi-1+ bi-2的奇偶性,同樣是奇向右、偶向左。

當得到方向性後,bi的值就是背向i的方向直到排列邊界這個區間裡比i小的數字的個數。如此就能得到鄰位對 換法的中介數。

舉例

原排列(839647521)生成中介數方法:

如右圖所示,

鄰位對換法 鄰位對換法

2的方向向左,

背向2的方向中比2小的數字有1個,b2=1

3的方向由b2為奇可以斷定向右,

背向3的方向中比3小的數字有0個,b3=0

4的方向由b2+b3為奇可以斷定向右,

背向4的方向中比4小的數字有1個,b4=1

5的方向由b4為奇可以斷定向右,

背向5的方向中比5小的數字有2個,b5=2

6的方向由b4+b5為奇可以斷定向右,

背向6的方向中比6小的數字有1個,b6=1

7的方向由b6為奇可以斷定向右,

背向7的方向中比7小的數字有3個,b7=3

8的方向由b6+b7為偶可以斷定向左,

背向8的方向中比8小的數字有7個,b8=7

9的方向由b8為奇可以斷定向右,

背向9的方向中比9小的數字有2個,b9=2

鄰位對換法 鄰位對換法

從中介數(10121372)得到原排列:

9:b8=7奇→9向右b9=2向右第3個空,

8:b6+b7=1+3=4偶→8向左b8=7 向左第8個空

7:b6=1奇→7向右,b7=3 向右第4個空

6:b4+b5=1+2=3奇→6向右,b6=1,向右第2個空

5:b4=1奇→5向右,b5=2,向右第3個空

4:b2+b3=1奇→4向右, b4=1,向右第2個空

3:b2=1奇→3向右, b3=0,向右第1個空

2:b2=1,向左第2個空

總之,如若要從原排列生成下一個排列,首先將原排列按上述方法轉換為中介數,再將中介數加一,得到新中介數,再將新中介數用以上方法換算為下一個排列。

一、算法的原理:

{1, 2, 3, …, n-1, n}的全排列可由{1, 2, 3, …, n-1}的全排列得出。具體做法為針對{1, 2, 3, …, n-1}的任意一個排列{a1, a2, a3, …, an-1},將n插入n個不同的位置得到:

{a1, a2, a3, …, an-1, n},

{a1, a2, a3, …, n, an-1},

……

{a1, a2, n, …, an-2, an-1},

{a1, n, a2, …, an-2, an-1},

{n, a1, a2, …, an-2, an-1}

即可得到{1, 2, 3, …, n-1, n}的全排列。

二、鄰位對換法:

1、 每個數有一個移動方向,向左或向右。如果數x的移動方向上最靠近它的數比它要小,那么x是可移動的。初始時序列為{1, 2, 3, …, n-1, n},方向都為向左。

2、 移動最大數n,直到不能移動為止,每改變一次位置輸出一個序列(此時n在最左邊或最右邊)。

3、 找當前可移動的最大數m。若能找到,移動m,把所有比m大的數的方向置反,返回第2步;若不能找到,算法停止。

舉例:

用sign[]來表示方向,sign[i]表示第i+1大的數的方向。用perm[]來表示序列,以n=4為例。

1 、初始狀態

sign:{0, 0, 0, 0}

perm:{1, 2, 3, 4}

移動n=4

perm:{1, 2, 4, 3}

perm:{1, 4, 2, 3}

perm:{4, 1, 2, 3} //能注意到,這4個是在{1, 2, 3}的基礎上添加4得到的排列

2 、找到可移動的最大數m=3 ,移動m ,把4 的方向置反

sign:{0, 0, 0, 1}

perm:{4, 1, 3, 2}

移動n=4

perm:{1, 4, 3, 2}

perm:{1, 3, 4, 2}

perm:{1, 3, 2, 4} //能注意到,這4個是在{1, 3, 2}的基礎上添加4得到的排列

3 、找到可移動的最大數m=3 ,移動m ,把4 的方向置反

sign:{0, 0, 0, 0}

perm:{3, 1, 2, 4}

移動n=4

perm:{3, 1, 4, 2}

perm:{3, 4, 1, 2}

perm:{4, 3, 1, 2} //能注意到,這4個是在{3, 1, 2}的基礎上添加4得到的排列

4、 找到可移動的最大數m=2 ,移動m ,把3, 4 的方向置反

sign:{0, 0, 1, 1}

perm:{4, 3, 2, 1}

移動n=4

perm:{3, 4, 2, 1}

perm:{3, 2, 4, 1}

perm:{3, 2, 1, 4} //能注意到,這4個是在{3, 2, 1}的基礎上添加4得到的排列

5、 找到可移動的最大數m=3 ,移動m ,把4 的方向置反

sign:{0, 0, 1, 0}

perm:{2, 3, 1, 4}

移動n=4

perm:{2, 3, 4, 1}

perm:{2, 4, 3, 1}

perm:{4, 2, 3, 1} //能注意到,這4個是在{2, 3, 1}的基礎上添加4得到的排列

6、 找到可移動的最大數m=3 ,移動m ,把4 的方向置反

sign:{0, 0, 1, 1}

perm: {4, 2, 1, 3}

移動n=4

perm: {2, 4, 1, 3}

perm: {2, 1, 4, 3}

perm: {2, 1, 3, 4} //能注意到,這4個是在{2, 1, 3}的基礎上添加4得到的排列

此時發現沒有可移動的數了,算法結束。

三、c語言實現示例:

#include <stdio.h>

#define MAXSIZE 100

int orientation[MAXSIZE];

char permData[MAXSIZE];

long count = 0;

void generatePearm(int n) {

int pos;

char temp;

if (permData[0] == ('a' + n - 1)) {

pos = 0;

while(pos < n - 1) {

count++;

//puts(permData);

temp = permData[pos];

permData[pos] = permData[pos+1];

permData[pos+1] = temp;

pos++;

}

count++;

//puts(permData);

} else {

pos = n - 1;

while (pos > 0) {

count++;

//puts(permData);

temp = permData[pos];

permData[pos] = permData[pos-1];

permData[pos-1] = temp;

pos--;

}

count++;

//puts(permData);

}

}

int nCantMove(int n) {

char max = 'a';

int pos = -1, i;

for (i = 1; i < n - 1; i++) {

if (permData[i] < max)

continue;

if ((orientation[permData[i]-'a'] && permData[i] > permData[i+1]) || (!orientation[permData[i]-'a'] && permData[i] > permData[i-1])) {

max = permData[i];

pos = i;

}

}

if ((permData[0] != 'a' + n - 1) && permData[0] > max && permData[0] > permData[1] && orientation[permData[0]-'a']) {

max = permData[0];

pos = 0;

}

if ((permData[n-1] != 'a' + n - 1) && permData[n-1] > max && permData[n-1] > permData[n-2] && !orientation[permData[n-1]-'a']) {

max = permData[n-1];

pos = n - 1;

}

if (max == 'a')

return 0;

if (orientation[max-'a']) {

permData[pos] = permData[pos+1];

permData[pos+1] = max;

} else {

permData[pos] = permData[pos-1];

permData[pos-1] = max;

}

for (i = 0; i < n; i++) {

if (permData[i] > max) {

orientation[permData[i]-'a'] = 1 - orientation[permData[i]-'a'];

}

}

return 1;

}

void nearPosExchGeneratePerm(int n) {

do {

generatePearm(n);

} while (nCantMove(n));

printf("%ld\n", count);

}

int main() {

int n, i;

scanf("%d", &n);

for (i = 0; i < n; i++) {

orientation[i] = 0;

permData[i] = 'a' + i;

}

permData[n] = '\0';

nearPosExchGeneratePerm(n);

return 0;

}

相關詞條

熱門詞條

聯絡我們