全排列的算法(二)——递增进位法
来源:互联网 发布:数据收集网站 编辑:程序博客网 时间:2024/05/22 12:54
2.递增进位法
集合{1、2、……n-1、n}的全排列中,只有原始排列12……n的各个元素的顺序是自然顺序,其余排列的元素的顺序都要改变。为了说明元素间顺序的改变,引入了排列的逆序的概念,它在行列式理论中起着重要的作用。
设集合{1,2,……,n}的一个排列是 p0p1……pn-1,如果对于任意的k和l(0≤k,l≤n-1),当k<l 有 pk>pl,则称数对(pk,pl)是排列 p0p1……pn-1 的一个逆序对。在排列 p0p1……pn-1 中,在元素 pj 左面比它大的的元素的个数为 qj( j=0,l,……,n-1),那么,称 q0,q1,……,qn-1为这个排列的逆序序列,而数 q0+q1+……+qn-1 叫做这个排列的逆序数。
例如,排列48625137中,1前面比它大的数有5个,2前面比它大的数有3个,……,因此,这个排列的的逆序序列是53402110,它的逆序数则是5+3+4+0+2+1+1+0=16。
因为在集合{1,2,…… ,n}中,至多有n-1个整数大于1,同样,至多有n-2个整数大于2,……,而没有其他的数大于n。所以,集合{1,2,……,n}的任何一个排列的逆序序列 q0,q1,……,qn-1 都满足条件:
0≤q0≤n-1,0≤q1≤n-2,……,0≤qn-2≤1,qn-1=0
于是,集合{1,2,……,n}的全排列中,逆序数最小的排列是123…… n,它的逆序数是0,而逆序数最大的排列是n(n-1)……321,它的逆序数 n(n-1)/2。
在排列与逆序序列之间存在一一对应关系,不同的逆序序列对应着不同的排列,所以我们可以从逆序序列生成对应的排列。下面是根据排列的逆序序列生成排列的算法。
用n个元素的数组p存放排列的元素,用数组q存放排列的逆序序列,当排列的逆序序列已知时,按下列步骤可以生成一个排列:
第一步:将数组 p全部初始化为0。
第二步:将1放在左起的第 q[0]个空位处。再将2放在余下空位中左起的第q[1]个空位处,……一般地,将k放在余下的空位中左起的第 q[k-1]个空位处,……,直到将n放入最后一个空位。
执行完上述步骤后,就得到了集合{1,2,…,n}的一个排列,它的逆序序列是q[0],q[1],……,q[n-1]。
n个数的全排列中第一个排列是123……n,它的逆序序列是0,0,……,0,将它看作是一个n位数00……0,给它加1,得到的是00……10,对应的逆序序列是0,0,……,1,0,根据它生成的排列是前一个排列的后继排列,继续加1,就可以生成全体排列了。应当注意的是,qn-1 总是保持为0,累加从 qn-2 开始,在累加时,当该数其中某一位超出它的表示范围时需要进位,但是注意到逆序序列所要满足的条件,所以进位方法需要采用一种称为递增进位的方法。一般来说,在递增进位法中,因为 0≤qi<n-i,所以,当 qn-i≥i 时就发生进位。例如,开始时,第一个逆序序列是00……00,对它进行累加,也就是将qn-2 加1,因为 qn-2必须小于1,所以当qn-2 再加1后,qn-2=2 就需要进位。同样,对于 qn-3 来说,当qn-3 =3 时也要进位,……。这就是将它称为递增进位法的原因。
递增进位算法生成全排列的程序代码如下:
//递增进位算法
//输入:排列元素个数n
//输出:n个元素的排列
#include<iostream>
#include <iomanip>
using namespace std;
void increase(int *p,int n); //递增进位排列算法
int invert(int *q,int n); //计算逆序序列及逆序数
void output(int *p,int n); //输出排列
int total; //排列计数变量
int main()
{
freopen("in.dat","r",stdin);
// freopen("out.dat","w",stdout);
int n,*p;
while(cin>>n)
{
p=new int[n];
perm(p,n);
}
return 0;
}
void increase(int *p,int n)
{
int *q=new int[n],i,j,k;
fill_n(q,n,0);
while(1)
{
fill_n(p,n,0); //数组初始化
for(i=0;i<n;i++) //数组空位计数
{
k=-1; //空位计数变量
for(j=0;j<n;j++)
{
if(p[j]==0)
k++;
if(k==q[i])
{
p[j]=i+1; //把数据填入空位
break;
}
}
}
output(p,n); //输出一个排列
if(!invert(q,n)) //全部排列都已生成,结束
break;
}
}
int invert(int *q,int n)
{
int i=n-2,m=0; //从qn-1开始
q[i]++; //累加1
while(q[i]==n-i) //进位
{
q[i--]=0;
q[i]++;
}
for(i=0;i<n;i++) //计算逆序数
m+=q[i];
return m;
}
void output(int *p,int n)
{
cout<<setw(10)<<++total<<": ";
for(int i=0;i<n;i++)
cout<<setw(3)<<p[i];
cout<<endl;
}
在该算法中,从逆序序列 0,0,……,0 开始生成全排列,最后一个排列的逆序序列是 n,……,2,1,0,将它加1(加在右起第二位上),进位结果是 0,0,……,0,回到第一个排列,所以是通过检测逆序数是否为0确定排列是否全部生成的。
- 全排列的算法(二)——递增进位法
- 全排列的算法(三)——递减进位法
- 全排列的算法(四)——n进位法
- 全排列及相关扩展算法(五)——递增(递减)进位制数求原排列算法
- 数组全排列算法(二)整型数组全排列——递归算法
- 全排列及相关扩展算法(二)——求字典序下一组排列及全排列算法
- 全排列的算法(六)——回溯法
- 全排列的算法(八)——序数法
- 全排列及相关扩展算法(六)——全排列最蛋疼的算法:邻位对换法
- 全排列的算法(七)——递归算法
- 全排列生成算法(二)
- 全排列及相关扩展算法(一)——基础的回溯递归实现全排列算法
- 数组全排列算法(一)字符串数组全排列——逐个追加组合算法
- 全排列-递增进位制数法
- 全排列的算法(五)——邻位互换法
- 算法理解——全排列算法
- 全排列(算法)
- 深度优先算法—取特定的全排列
- 树的括号表示
- UILable颜色设置
- MFC程序最小化到系统托盘
- NoSQL数据库家族图谱
- javaFX初探(Camera)
- 全排列的算法(二)——递增进位法
- ZOJ 1622 Switch
- spring注解完整版
- 12个实用的 Javascript 奇技淫巧
- java内存结构
- Android NDK 安装与配置
- java 单例模式及运用
- 20-NSString-foundation
- 如何使用VS发布ASP.NET应用到Windows Azure