康托展开

1. 康托展开

X = A[0] * (n-1)! + A[1] * (n-2)! + … + A[n-1] * 0! 

A[i] 指的是位于位置i后面的数小于A[i]值的个数,后面乘的就是后面还有多少个数的阶乘

tips:这个算出来的数康拖展开值,是在所有排列次序 - 1的值,因此X+1即为在全排列中的次序,康托展开可逆(自然数 -> 序列)。

▲:康托展开的实质是计算当前排列在所有由小到大全排列中的名次。

▲:康托展开的原理就是从某个排序组合的首位开始,找出比当前这位数的个数,且之前没有出现过的数的个数乘以对应的阶乘,结果累加就是我们要求的值。//阶乘就理解为以前学过的排列组合,而不同数的阶乘也对数的次序进行了划分,如n=5时,1开头的数,就分布在1到4!。2开头的数,就分布在4!+1到2*4!

 1 int cantor(int *a,int n)
 2 {
 3     int ans=0;
 4     for(int i=0;i<n;i++)
 5     {
 6         int x=0,c=1,m=1;
 7         for(int j=i+1;j<n;j++)
 8         {
 9             if(a[j]<a[i]) x++;
10             m*=c++;
11         }
12         ans+=m*x;
13     }
14     return ans;
15 }

 

2. 逆康托展开

对数X逐个mod(n-i)!,商为几,则表示在第i位(i从第一位开始)后有几个数,然后用余数继续除阶乘求余。

▲:原理结合上面,可知在求余数的过程中,其实就是在给这些数定位

 1 void obcantor(int x,int n) //O(n^2)
 2 {
 3     vector<int>v; //存放当前可选数
 4     vector<int>a; //所求序列
 5     int ft[10],id=1;
 6     ft[0]=1;
 7     for(int i=1;i<=n;i++)
 8     {
 9         v.push_back(i);
10         id*=i;
11         ft[i]=id;
12     }
13     for(int i=n-1;i>=0;i--)
14     {
15         int s=x/ft[i];
16         int t=x%ft[i];
17         x=t;
18         sort(v.begin(),v.end());
19         a.push_back(v[s]); //剩余数里第t+1个
20         v.erase(v.begin()+s);
21     }
22     //for(int i=0;i<n;i++)
23     //    cout<<a[i]<<" ";
24 }

 

posted @ 2019-07-27 19:26  XXrl  阅读(261)  评论(0编辑  收藏  举报