关于康托展开的简单学习(小白的第一篇)
首先关于,康拓展开,我们要知道,这个关于全排列的一个用法。相信,我们在以后的算法学习中,会有所运用。
举例:
首先,我们要知道,对于(1,2,3)的全排列,有这几种,分别是,(1,2,3)、(1,3,2)、(2,1,3)、(2,3,1)、(3,1,2)、(3,2 ,1)这个顺序是根据字典序排列的。假如我们想知道它全排列的(2,3,1)是第几项,我们除了全部列出来,我们还可以通过康托展开知道。
首先它的公式是这样的:
X = A[0] * (n-1)! + A[1] * (n-2)! + … + A[n-1] * 0!
- A[i] 是当前i的逆序数(就是后面比它小的个数)
- 假如是(2,3,1)对因的A序列就是(1,1,0)
那么这个结果就是,12!+11!+0*0!=3,这个和我们算的不符! - 原因是,这个算出来的是,全排列减一的值,不信的计算一下(1,2,3)是不是0,所以,**我们的计算结果要+1!**那么结果就是4喽
接着,我们有康托展开式,就会有逆展开式,知道数字,就对应的展开式,嘿嘿!
假如我们知道它是4,且知道它是(123)(n=3),我们就可以通过取余,得到结果,1. 4/2! = 1(逆序数)。。。。。1 (下一次计算的值) 则此时可知为2,因为逆序数为1,此时只有2满足。
4. 1/1! = 1。。。。。0 此时可知是3,因为逆序数是1,也只有3满足。
5. 之后,就之后1了。
关于代码,我尝试写了写,不专业哦!
int ktzk (int a[],int n) {
int ans =0;
for (int i=0;i<n;i++) {
int x=0;
int c=1,m=1;
for (int j=i+1;j<n;j++) {
if (a[j]<a[i]) x++;
m=m*c;
c++;
//这里通过算逆序数的过程,顺便算了,阶乘!
}
and += x*m;
}
return ans;
}
static const int FAC[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880}; // 阶乘
//康托展开逆运算
void decantor(int x, int n)
{
vector<int> v; // 存放当前可选数
vector<int> a; // 所求排列组合
for(int i=1;i<=n;i++)
v.push_back(i);
for(int i=m;i>=1;i--)
{
int r = x % FAC[i-1];
int t = x / FAC[i-1];
x = r;
sort(v.begin(),v.end());// 从小到大排序
a.push_back(v[t]); // 剩余数里第t+1个数为当前位
v.erase(v.begin()+t); // 移除选做当前位的数
}
}
//这个代码是大佬写的,我逆展开,真的写的没这么好!
我直接把大佬的链接发出来,果然还是要看看牛人的博客!
大佬的博客