关于这个我主要学习了两个内容,关于全跑排列的
一个是任意一个全排列数在所有全排列数中排第几(从小到大或从大到小)
一个是对于由数字1-n组成的全排列数(从小到大或从大到小),任意一位全排列数是多少(求第x个全排列数)
以下例子全都以从小到大为序
求位置
举例
51324
它是数字1-5组成的全排列数
它的位置就是比它晓得全排列数的个数+1
那么比它小的全排列数有多少个呢??
逐位讨论
对于第一位,它小于5时(有4个数字),无论之后的四位怎么排(共4!种排法)都比它小
于是 res+=4*4!
那么第一位取5时
对于第二位,它小于1时同上
但是1-5没有数字小于1
于是 res+=0*3!
那么前两位取51时
对于第三位,小于3时同上,但由于1已经用过了,所以只有一个2比它小且没被用过
于是 res+=1*2!
前三位取513
对于第四位,小于2同上
于是 res+=0*1!
前四位取5132
最后一位只有一种方案,因为除四之外所有数全被用过了
于是 res+=1
res+1即为所求
总结
不难得出,对于1-n的任意全展开数
若第i位数num[i],前i-1位用过的比i小的数为o
res+=(num[i-1]-o)*(n-i)!
说明:
因为当前i-1位数确定时,只有比num[i]小的数字放在i位上才能够随心所欲地组合
然而如果对于x<num[i],x出现在了之前i-1位上时,这一位用的数就要-1
所以转换为代码
int WEIZHI(int *num)
{
int n,res=0;//n为这个数的位数
for(int i=1;i<=n;i++)
{
use=num[i];
for(int p=1;p<=i;p++)
if(num[p]<=num[i])use--;//可以将数字为num[i]的情况也减去
res+=use*JC[num-i];//JC存(num-i)!
}
return res;
}
求第x个全排列数
有了上面的基础,此处的算法会好推很多
对于任意1-n组成的第num个全排列数
都有num=a*(n-1)!+b*(n-2)!+……+z*0!+1
举例
51324
51324的位置=4*4!+0*3!+(2-1)*2!+(1-1)*1!+(3-3)*1!+1
注意此处括号内的减数,这就是小于a、b、c...的已经出现的数的个数之和
第一位为有4个小于它且之前没出现过的数->5
第二位为有0个小于它且之前没出现过的数->1
第三位为有1个小于它且之前没出现过的数
因为1出现过了,所以这个比它小的数为2,这个数为3
.
.
.
51324的位置为97
那么第97个数根据以上过程反推
第一位(97-1)/(4!)=4...3 ==>为5
第二位(97-1-4*4!)=3 (上式余数)
3/(3!)=0...2 ==>为1(见上述过程)
.
.
.
所以对于1-n的全排列数中的第x个
第i位为
上次运算的余数c/(n-i)! 的整数商t
从1-n排列的数中没出现的第t个数
(很难懂,但是我的描述能力仅限于此)
附上代码
int QIUSHU(int x,int n)//1-n全排列数,第x个
{
int num[n];bool mark[n];//num存结果 mark标记已经出现过的数
for(int i=1;i<=n;i++)
{
pos=x/JC[n-i];//JC存阶乘
for(i=1;i<=n;i++)
if(!mark[i])
{
if(!pos)break;
pos--;
}
num[i]=1;
mark[i]=1;
x=x%JC[n-i];
}
for(int i=1;i<=n;i++)printf("%d",num[i]);
}