康托展开及其逆运算
- 康托展开:判断这个数在其所有数从小到大全排列中排在第几位。换句话说也就是比他小的数字有多少个。
x = an*(n-1)! + an-1*(n-2)! + …… + a1*0!
其中x表示当前数字在其全排列中占第几位。
an表示比当前在此位置的数字小的数字,且出现在当前序列中的还排列在这个数字之后的数字有几个
n-1表示从当前序列末尾开始数在第几位,末尾为零,逐渐+1
例如
4312:
比4小的数字有三个, 分别是3,2,1,都没有出现过,而且出现在第三位,所有是3*3!=18
比3小的数字有两个, 分别是2,1,并且都没有出现过,而在序列中排第二位,所以是2*2!=4
比1小的数字有0个,且1在序列中排在第一位, 所以是0*1!=0
比2小的数字有1个,是1,当时在他之前已经出现了,而且2排在第零位,所以是0*0!=0
0+0+4+18 = 22
所以比当前数字小的数字有22个,当前数字排列在第23位
C语言实现:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char s[5] = "4312";
int a[4] = {0};
int sum = 0;
a[0] = 1;
a[1] = 1;
for(int i = 2; i < 4; i++) {
a[i] = a[i-1] * i;
}
for(int i = 0; i < 4; i++) {
int temp = 0;
//从当前字符到字符串末尾查找比他小的数字的个数
for(int j = i+1; j < 4; j++) {
if(s[j] < s[i]){
temp ++;
}
}
//将比他小的数字和当前数字所在的位数的阶乘相乘并相加
sum += temp * a[4-i-1];
}
printf("%d", sum);
return 0;
}
- 康托运算的逆运算
康托运算求逆运算的方法
例如我们要检验上面康托运算的4312
是否正确,所以我们要求出1234的第23中排列方式
首先23-1 = 22
按照上面的说法第一个数字是在第三位,所以我们用22/3! = 3 余数为4,显然比第一个数字小的数字有3, 所以第一个数字是4
4/2!= 2, 余数为0,所以比第二个数字小的数字有两个,所以第二个数字是3
0/1!= 0, 余数为0, 所以比第三个小的数字有0个,所以第三个数字是1
0/0!= 0,所以比第4个数字小的数字有0个,1已经存在了,所以第4个数字是 2
所以第23中排列为4312
C语言实现
#include <stdio.h>
#include <stdlib.h>
int main()
{
char s[5];
int n = 21;
int i, j;
int visit[4] = {0};
int p[4] = {0};
p[0] = 1;
p[1] = 1;
//求每位数的阶乘
for(i = 2; i < 4; i++ ) {
p[i] = p[i-1] * i;
}
//康托运算的逆运算
n --;
for(i = 0; i < 4; i ++) {
int t = n / p[4-i-1];
for(j = 0; j < 4; j++) {
if( !visit[j] ) {
if(t == 0){
break;
}
//利用循环寻找比0大t个数字的数字
t--;
}
}
visit[j] = 1;
s[i] = '0' + j + 1;
n %= p[4-i-1];
}
s[i+1] = '\0';
printf("%s", s);
return 0;
}