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 }