您的位置:首页 > 其它

非有序全排列生成算法集锦

2009-01-20 15:59 363 查看
/*
Name: 非有序全排列生成算法集锦
Copyright: 始发于goal00001111的专栏;允许自由转载,但必须注明作者和出处
Author: goal00001111
Date: 20-01-09 15:55
Description: 实现了七种非有序全排列生成算法。
有关算法的分析讨论详见拙作《非有序全排列生成算法》: http://blog.csdn.net/goal00001111/archive/2009/01/20/3839688.aspx */
#include<iostream>
#include <time.h>

using namespace std;

void Print(int a[], int n);
void Permutation1(int n);
void Permutation2(int n);
void Permutation3(int n);
void Permutation4(int n);
void Permutation5(int n);
void Permutation6(int n);
void Permutation7(int n);
void Recursion1(int a[], int n, int k);
void Recursion2(int a[], int n, int k);
bool Move(int a[], bool p[], int n);
void DiZYingShe(int yShe[], int diZ[], int n);
void DiJYingShe(int yShe[], int diJ[], int n);

int main()
{
int n = 10;
time_t startTime;
time_t endTime;

time(&startTime);
Permutation1(n); //普通递归算法
time(&endTime);
cout << "time 1 = " << difftime(endTime, startTime) << endl << endl;

time(&startTime);
Permutation2(n); //全排列循环移位法
time(&endTime);
cout << "time 2 = " << difftime(endTime, startTime) << endl << endl;

time(&startTime);
Permutation3(n); //排列邻位对换法
time(&endTime);
cout << "time 3 = " << difftime(endTime, startTime) << endl << endl;

time(&startTime);
Permutation4(n); //递增进位排列生成算法
time(&endTime);
cout << "time 4 = " << difftime(endTime, startTime) << endl << endl;

time(&startTime);
Permutation5(n); //递减进位排列生成算法
time(&endTime);
cout << "time 5 = " << difftime(endTime, startTime) << endl << endl;

time(&startTime);
Permutation6(n); //循环左移排列生成算法
time(&endTime);
cout << "time 6 = " << difftime(endTime, startTime) << endl << endl;

time(&startTime);
Permutation7(n); //颠倒的协词典顺序算法
time(&endTime);
cout << "time 7 = " << difftime(endTime, startTime) << endl << endl;

system("pause");
return 0;
}

void Print(int a[], int n)
{
for (int i=0; i<n; i++)
cout << a[i];
cout << endl;
}
////////////////////////////普通递归算法////////////////////////////////////
/*
函数名称:Permutation
函数功能:普通递归算法:输出n个数的所有全排列
输入变量:int n:1,2,3,...,n共n个自然数
输出变量:无
*/
void Permutation1(int n)
{
int *a = new int
;//用来存储n个自然数
for (int i=0; i<n; i++) //存储全排列的元素值
a[i] = i + 1;

Recursion1(a, n, n-1); //调用递归函数

Print(a, n); //输出最后一个全排列

delete []a;
}

/*
函数名称:Recursion
函数功能:递归输出n个数的所有全排列
输入变量:int a[]:存储了1,2,3,...,n共n个自然数的数组
int n:数组a[]的长度
int k:正在处理的k个元素所组成的排列
输出变量:无
*/
void Recursion1(int a[], int n, int k)
{
if (k == 0) //排列只有一个元素a[k],直接输出
;// Print(a, n);
else
{
int temp;
for (int i=0; i<=k; i++) //穷举,依次让第k个元素与前面的元素交换
{
temp = a[i];
a[i] = a[k];
a[k] = temp;

Recursion1(a, n, k-1); //递归生成k-1个元素的全排列

temp = a[i]; //再换回来
a[i] = a[k];
a[k] = temp;
}
}
}

//////////////////////////////////全排列循环移位法///////////////////////
/*
函数名称:Permutation
函数功能:全排列循环移位法:输出n个数的所有全排列
输入变量:int n:1,2,3,...,n共n个自然数
输出变量:无
*/
void Permutation2(int n)
{
int *a = new int
;//用来存储n个自然数
for (int i=0; i<n; i++) //存储全排列的元素值,并计算全排列的数量
{
a[i] = i + 1;
}

Recursion2(a, n, 1);

Print(a, n); //输出最后一个全排列

delete []a;
}

/*
函数名称:Recursion
函数功能:递归输出n个数的所有全排列
输入变量:int a[]:存储了1,2,3,...,n共n个自然数的数组
int n:数组a[]的长度
int k:正在处理的k个元素所组成的排列
输出变量:无

*/
void Recursion2(int a[], int n, int k)
{
if (k > n)
;//Print(a, n);
else
{
int temp;
for (int i=0; i<k; i++)//循环左移
{
temp = a[0];
for (int j=1; j<k; j++)
a[j-1] = a[j];
a[k-1] = temp;

Recursion2(a, n, k+1);
}
}
}

/////////////////////////排列邻位对换法////////////////////////////////
/*
函数名称:Permutation
函数功能:排列邻位对换法:输出n个数的所有全排列
输入变量:int n:1,2,3,...,n共n个自然数
输出变量:无
*/
void Permutation3(int n)
{
int *a = new int
; //用来存储n个自然数
bool *p = new bool
; //用来存储n个元素的指向:向左为false,向右为true
for (int i=0; i<n; i++) //存储全排列的元素值,并计算全排列的数量
{
a[i] = i + 1;
p[i] = false; //开始均指向左侧
}

do
{
// Print(a, n); //输出第一个全排列
if (n == a[n-1])//若n在最右侧,将其逐次与左侧的元素交换,得到n - 1个新的排列
{
for (int i=n-1; i>0; i--)
{
int temp = a[i]; a[i] = a[i-1]; a[i-1] = temp;
bool flag = p[i]; p[i] = p[i-1]; p[i-1] = flag;
// Print(a, n);
}
}
else //若n在最左侧,将其逐次与右侧的元素交换,得到n - 1个新的排列
{
for (int i=1; i<n; i++)
{
int temp = a[i]; a[i] = a[i-1]; a[i-1] = temp;
bool flag = p[i]; p[i] = p[i-1]; p[i-1] = flag;
// Print(a, n);
}
}
} while (Move(a, p, n));

Print(a, n); //输出最后一个全排列

delete []a;
delete []p;
}

/*
函数名称:Move
函数功能:寻找最大可移数,可移数m,将m与其箭头所指的邻数互换位置,
并将所得新排列中所有比m大的数p的方向调整
输入变量:int a[]:存储了1,2,3,...,n共n个自然数的数组
bool p[]:存储了n个元素的指向的数组:向左为false,向右为true
int n:数组a[]的长度
输出变量:排列中存在最大可移数,则做了相关操作后返回真,否则直接返回假
*/
bool Move(int a[], bool p[], int n)
{
int max = 1;
int pos = -1;

for (int i=0; i<n; i++)
{
if (a[i] < max)
continue;
if ((p[i] && i < n-1 && a[i] > a[i+1]) || //指向右侧
(!p[i] && i > 0 && a[i] > a[i-1])) //指向左侧
{
max = a[i];
pos = i;
}
}

if (pos == -1) //都不能移动
return false;

//与其箭头所指的邻数互换位置
if (p[pos]) //指向右侧
{
int temp = a[pos]; a[pos] = a[pos+1]; a[pos+1] = temp;
bool flag = p[pos]; p[pos] = p[pos+1]; p[pos+1] = flag;
}
else //指向左侧
{
int temp = a[pos]; a[pos] = a[pos-1]; a[pos-1] = temp;
bool flag = p[pos]; p[pos] = p[pos-1]; p[pos-1] = flag;
}

//将所得排列中所有比max大的数p的方向调整
for (int i=0; i<n; i++)
{
if (a[i] > max)
p[i] = !p[i];
}

return true;
}

//////////////////////////////递增进位排列生成算法////////////////////////
/*
函数名称:Permutation
函数功能:递增进位排列生成算法:输出n个数的所有全排列
输入变量:int n:1,2,3,...,n共n个自然数
输出变量:无
*/
void Permutation4(int n)
{
int *p = new int
;//用来存储各个全排列的值,p[i]=i!
p[0] = 1;
for (int i=1; i<n; i++)
{
p[i] = (i + 1) * p[i-1]; //cout << p[i] << " ";
}

int *b = new int
; //用来存储新的全排列
int *yShe = new int[n-1]; //将原中介数加上序号i的递增进位制数得到新的映射
int *diZ = new int[n-1]; //用来存储序号i的递增进位制数,设所有元素的初值为0
for (int i=0; i<n-1; i++)
{
diZ[i] = yShe[i] = 0;
}

diZ[n-2] = 1; //存储序号1,表示每次递增1
for (int c=0; c<p[n-1]; c++)
{
DiZYingShe(yShe, diZ, n); //每次递增1后得到得到新的映射

for (int i=0; i<n; i++) //新的全排列空格初始化
b[i] = 0;

int num = n;
for (int j=0; j<n-1; j++) //获取前n-1个数字
{
int s = 0;
int k = n - 1;
while (s < yShe[j])
{
if (b[k] == 0) //该处未填数字
s++;
k--;
}
while (b[k] != 0) //跳过已填数字
k--;
b[k] = num--;
}
for (int i=0; i<n; i++)//填充最后一个数字1
{
if (b[i] == 0)
{
b[i] = 1;
break;
}
}

// Print(b, n);
}

Print(b, n); //输出最后一个全排列

delete []b;
delete []p;
delete []diZ;
delete []yShe;
}

/*
函数名称:DiZYingShe
函数功能:递增进位排列生成算法的加法运算:yShe[] += dZJ[]
输入变量:int yShe[]:原递增进制中介数;
int diZ[]:递增进制数加数
int len:最大自然数n的值
输出变量:int yShe[]:新的递增进制中介数
*/
void DiZYingShe(int yShe[], int diZ[], int n)
{
int pos = n - 2;
int r = 0;
while (pos >= 0)
{
yShe[pos] += diZ[pos] + r;
if (yShe[pos] >= n - pos)
{
yShe[pos] -= n - pos;
r = 1;
}
else
r = 0;
pos--;
}
}

///////////////////////////递减进位排列生成算法///////////////////////////////
/*
函数名称:Permutation
函数功能:递减进位排列生成算法:输出n个数的所有全排列
输入变量:int n:1,2,3,...,n共n个自然数
输出变量:无
*/
void Permutation5(int n)
{
int *p = new int
;//用来存储各个全排列的值,p[i]=i!
p[0] = 1;
for (int i=1; i<n; i++)
{
p[i] = (i + 1) * p[i-1]; //cout << p[i] << " ";
}

int *b = new int
;//用来存储新的全排列
int *yShe = new int[n-1]; //将原中介数加上序号i的递减进位制数得到新的映射
int *diJ = new int[n-1];
for (int i=0; i<n-1; i++)
{
diJ[i] = yShe[i] = 0;
}

diJ[n-2] = 1;
for (int c=0; c<p[n-1]; c++)
{
DiJYingShe(yShe, diJ, n); //每次递增1

for (int i=0; i<n; i++) //新的全排列空格初始化
b[i] = 0;

int num = n;
for (int j=n-2; j>=0; j--) //获取前n-1个数字
{
int s = 0;
int k = n - 1;
while (s < yShe[j])
{
if (b[k] == 0) //该处未填数字
s++;
k--;
}
while (b[k] != 0) //跳过已填数字
k--;
b[k] = num--;
}
for (int i=0; i<n; i++)//填充最后一个数字1
{
if (b[i] == 0)
{
b[i] = 1;
break;
}
}

//Print(b, n);
}

Print(b, n); //输出最后一个全排列

delete []b;
delete []p;
delete []diJ;
delete []yShe;
}

/*
函数名称:DiJYingShe
函数功能:递减进位排列生成算法的加法运算:yShe[] += dZJ[]
输入变量:int yShe[]:原递减进制中介数;
int diJ[]:递减进制数加数
int n:最大自然数n的值
输出变量:int yShe[]:新的递减进制中介数
*/
void DiJYingShe(int yShe[], int diJ[], int n)
{
int pos = n - 2;
int r = 0;
while (pos >= 0)
{
yShe[pos] += diJ[pos] + r;
if (yShe[pos] >= pos + 2)
{
yShe[pos] -= pos + 2;
r = 1;
}
else
r = 0;
pos--;
}
}

////////////////////////////////循环左移排列生成算法///////////////////////////////
/*
函数名称:Permutation
函数功能:循环左移排列生成算法:输出n个数的所有全排列
输入变量:int n:1,2,3,...,n共n个自然数
输出变量:无
*/
void Permutation6(int n)
{
int *p = new int
;//用来存储各个全排列的值,p[i]=i!
p[0] = 1;
for (int i=1; i<n; i++)
{
p[i] = (i + 1) * p[i-1]; //cout << p[i] << " ";
}

int *b = new int
;//用来存储新的全排列
int *yShe = new int[n-1]; //将原中介数加上序号i的递减进位制数得到新的映射
int *diJ = new int[n-1];
for (int i=0; i<n-1; i++)
{
diJ[i] = yShe[i] = 0;
}

diJ[n-2] = 1;
for (int c=0; c<p[n-1]; c++)
{
DiJYingShe(yShe, diJ, n); //每次递增1

for (int i=0; i<n; i++) //新的全排列空格初始化
b[i] = 0;

int num = n;
int pos = n - 1;
for (int j=n-2; j>=0; j--) //获取前n-1个数字
{
int s = 0;
int k = pos;
while (k >= 0 && s < yShe[j])
{
if (b[k] == 0) //该处未填数字
s++;
k--;
}

//跳过已填数字
while (k >= 0 && b[k] != 0)
k--;
if (b[k] != 0)//如果到了最左侧还没有找到适合的位置,从最右侧继续分析
{
k = n - 1;
while (k > pos && b[k] != 0)//跳过非空位:b[j] == 0表示j位置是空位
k--;
}

//如果到了最左侧还没有找到适合的位置,从最右侧继续累积s
if (s < yShe[j])
{
k = n - 1;
while (k >= 0 && s < yShe[j])
{
if (b[k] == 0)
s++;
k--;
}
while (k >= 0 && b[k] != 0) //跳过已填数字
k--;
}
b[k] = num--;
pos = k;
}

for (int i=0; i<n; i++)//填充最后一个数字1
{
if (b[i] == 0)
{
b[i] = 1;
break;
}
}

// Print(b, n);
}

Print(b, n); //输出最后一个全排列

delete []b;
delete []p;
delete []diJ;
delete []yShe;
}

/////////////////////////////////颠倒的协词典顺序算法////////////////
/*
函数名称:Permutation
函数功能:颠倒的协词典顺序算法:逆序输出n个数的所有全排列
输入变量:int n:1,2,3,...,n共n个自然数
输出变量:无
*/
void Permutation7(int n)
{
int *a = new int
;//用来存储n个自然数
for (int i=0; i<n; i++) //存储全排列的元素值,并计算全排列的数量
{
a[i] = i + 1;
}

int temp, left, right;
while (1)
{
// Print(a, n);
for (right=1; right<n; right++)//找出第一个比右边数字小的数字的序号right-1
{
if (a[right-1] < a[right])
break;
}
if (right == n) //表示已经输出所有全排列
break;
//找到右边界左边数组中比a[right]小的最大的元素,这个就是要取代a[right]的元素
for (left=0; a[left] >= a[right]; left++)
;
temp = a[left]; a[left] = a[right]; a[right] = temp; //交换a[right]和a[left]

left = 0;
right--; //右边界减1,保证此时左右边界之间的元素是一个递减排列
while (left < right)//逆置左右边界之间的元素,使其按增序排列
{
temp = a[left]; a[left] = a[right]; a[right] = temp;
left++;
right--;
}
}

Print(a, n);

delete []a;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: