为了账号安全,请及时绑定邮箱和手机立即绑定

全排列及相关扩展算法(五)——递增(递减)进位制数求原排列算法

标签:
C C++

1.引入原因:通过上一章我们意识到通过原始中介数求原排列并不是那么的方便,于是我们延伸出几种新的中介数算法来方便计算原排列。

2.递增进位制数法:递增进位制是指数字的进制随着数字位置的不同递增,一般的,最右位的进制为2,向左依次增1。用递增进位制数法改造的中介数即:我们不按照从左到右的顺序排,而是按照数字的大小来排。以上一章{ 7,6,8,3,4,5,1,2 }为例,其原始中介数为{6,5,5,2,2,2,0,(0)},(原中介数由于最后一位恒等于0所以一直忽略不计,这里再加上)将其按照原来位上数字的大小排序变成新的中介数为:{5, 6, 5, 2, 2, 2, 0, 0},不难理解,每一个位上的数都不会超过其位数(因为其位取决于其真实数,而其值取决于其中介数),故称其为递增进位制数。

求递增进位制数代码:

int* get_permutation_medium_plus(int A[], int n)
{
int* temp = new int[n];
 
for (int i = 0; i < n; i++)
{
temp[n-A[i]] = 0;
for (int j = i + 1; j <= n - 1; j++)
{
if (A[j] < A[i])
{
temp[n-A[i]]++;
}
}
}
 
return temp;
}

文末参考文档中有提到这种递增进位中介数通过下述算法求出一种序号,但不是字典序,不是字典序!

int get_permutation_rank_plus(int medium[], int n)
{
int rank = 0;
for (int i = 0; i < n-1; i++)
{
rank += medium[i];
rank *= n - i -1;
}
 
return rank;
}

其实就是转化成进制为位数n*k[n-1]的数,转化后每个位的进制变成0(0)、1(1)、2(1*2)、6(2*3)、24(6*4)、120(24*5)……

外部调用:

int A[] = { 4,3,2,1 };
int n = sizeof(A) / sizeof(A[0]);
//用STL模版函数遍历
while (next_permutation(A, n)||!Count)
{
printf("原排列:  ");
Print(A, n);
printf("中介数:  ");
int *medium = get_permutation_medium_plus(A, n);
Print(medium, n);
int rank = get_permutation_rank_plus(medium, n);
printf("新序号:  %d\n", rank);
printf("---------------\n");
Count++;
}

https://img1.sycdn.imooc.com//5b3e2dae00015a6a01590634.jpg

https://img1.sycdn.imooc.com//5b3e2dd80001c1bf01580325.jpg

https://img1.sycdn.imooc.com//5b3e2df600014b5401590516.jpg

然而看起来并没有什么卵用

https://img1.sycdn.imooc.com//5b3e2e0f00019bcb01110096.jpg

通过这种中介数求原排列非常的简便,中介数k[i]表示真实数n-i处于从右往左数第i个(之前已经存在的数不算),我们称此为空格法。

例如求342221的原排列

①__ __ ____ __ __ __第7位数字是3,我们从右向左数3个空格,再往前数一个空格,空格中填入对应的位数7。

②__ __ __7 __ __ __。第6位数字是4,我们从右向左数4个空格,其中已经放上数的不算空格,再往前数一个空格,空格中填入6。

③__ 6 __7 __ __ __。接下来的步骤也是一样的,第5位的数字是2,数2个空格,再往前数一个空格填入5。

④__ 6 __7 5 __ __。第4位是2,数2个空格再往前数一个空格填入4。

⑤__ 6 4 7 5 __ __。第3位是2,数2个空格再往前数一个空格填入3。

⑥3 6 4 7 5 __ __。第2位是1,数1个空格,再往前数一个空格填入2。

⑦3 6 4 7 5 2 __。第1位是0,数0个空格,再往前数一个空格填入1。

由此,我们得到了原来的排列3 6 4 7 5 2 1。

代码:

int* get_permutation_plus(int medium[], int n)
{
int* temp = new int[n];
memset(temp, 0, n * sizeof(int));
for (int i = 0; i < n; i++)
{
int empty = -1,j=n;//防止末尾已经被占的情况故提前一位
while (empty < medium[i] && j >= 0)
{
j--;
if (temp[j] <= 0)
{
empty++;
}
}
temp[j] = n - i;
}
 
return temp;
}

https://img1.sycdn.imooc.com//5b3e2e4d0001ee2b08530391.jpg

https://img1.sycdn.imooc.com//5b3e2e630001683608530333.jpg

3.递减进位制数法:

考虑到递增进制法中低位的进制都比较低,在求下一个排列时,中介数加1往往会导致很多的进位,计算比较麻烦,所以有了递减进位,实际上就是将递增进位制的中介数倒过来即可。这使得通过遍历递减进位制法的中介数得到全排列算法,相比于用递增进位制法较简单。


4.参考文档

https://wenku.baidu.com/view/8c79a2facc17552706220880.html


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消