康托展开详解

康托展开解释

通俗讲康托展开就是求解一个排列序号,比如123456,序号为1。123465序号为2,就是给你一个长度为n的序列你能求出第k字典序大小的排列。

康托展开公式:

X = a n ( n − 1 ) ! + a n − 1 ( n − 2 ) ! + . . . + a 1 0 ! X = a_n(n-1)!+a_{n-1}(n-2)!+...+a_10! X=an(n1)!+an1(n2)!+...+a10!
a i a_i ai代表的是从右往左数比当前这个数字小的并且没有出现过的数字个数。
例如:15432, 对于第2位5来说 a 2 a_2 a2 = {2,3,4} = 3;

首先你要知道,一个长度位n的序列他的全排列的数量S = 1*2*3*...*n;

24315这个序列举例算一下他是第几大的字典序排列。

这里我们申明一下康托展开的k是从0开始计算的,后面告诉大家为什么。

按照公式:
a 1 a_1 a1 = {1} = 1,因为当前只有2出现过,比2小的只有1,然后后面又三个数,那么比2开头还小的序列数:1 * 4!
a 2 a_2 a2 = {3, 1} = 2, 比2,4开头还小的序列有2! * 3!;
a 3 a_3 a3 = {1} = 1, 比2,4,3开头还小的序列有1! * 2!;
a 4 a_4 a4 = {} = 0, 比2,4,3,1开头还小的序列有0! * 1!;
a 5 a_5 a5 = {} = 0, 比2,4,3,1,5开头还小的序列有0! * 0!;

最终答案是:1*4!+2!*3!+1!*2!+0!*1!+0!*0 = 39;

为什么是从0开始计数:12345 = 04!+ 03!+ 02!+ 01!+0*0! = 0

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
 

int f[20];
void jie_cheng(int n)
{
    f[0] = f[1] = 1; 
    for(int i = 2; i <= n; i++) f[i] = f[i - 1] * i;
}
 
string str;
int kangtuo()
{
    int ans = 1;  
    for(int i = 0; i < len; i++){
        int tmp = 0;
 
        for(int j = i + 1; j < len; j++)
        {
            if(str[i] > str[j]) tmp++;
        }
        ans += tmp * f[len - i - 1];
    }
    return ans;
}
 
int main()
{
    jie_cheng(10);
    str = "24315";
    cout<<kangtuo()<<endl;
}

逆康托展开

如:12345求39大的排列
∵ \because 从0开始计数,我们就是开始从38开始算

38/4! = 1…14 ,{1, 2, 3, 4, 5}你要选一个数出来只有一个比它小的数所以第一位我们选2。
14/3!= 2…2,{1, 3, 4, 5},你要选一个数出来只有一个比它小的数所以第二位我们选4。
2/2! = 1…0, {1, 3, 5}, 你要选一个数出来只有一个比它小的数所以第三位我们选3。
0/1! = 0…0,{1, 5},你要选一个数出来没有一个比它小的数所以第四位我们选1。
最后就只有选5了。
所以39大的排列就是24315了

/***** 这里以字符串进行展示  字符串可泛化性好 ******/
 
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
 
int f[20];
int x, num;
 
void jie_cheng(int n)
{
    f[0] = f[1] = 1; 
    for(int i = 2; i <= n; i++) f[i] = f[i - 1] * i;
}
 
vector<char> vec; 
void rev_kangtuo(int k) 
{
    int n = vec.size(), len = 0;
    string ans = "";
    k--; 
    for(int i = 1; i <= n; i++){
        int t = k / f[n - i]; 
        k %= f[n - i];        
        ans += vec[t] ; 
        vec.erase(vec.begin() + t); 
    }
    cout << ans << '\n';
}
 
int main()
{
    jie_cheng(10); 
    scanf("%d", &x); 
    for(int i = 1; i <= 10; i++)
    {
    	if(x / f[i] == 0) 
    	{
    		num = i;
    		break;
    	}
    }
    for(int i = 1; i <= num; i++) vec.push_back(i + '0');
    rev_kangtuo(x);
}

参考:链接: link

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值