组合数学——康托展开和逆康托展开

组合数学——康托展开和逆康托展开

康托展开运算

                    

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

康托展开举例

  5个数的排列组合中,计算  34152  的康托展开值。

首位是3,则小于 3  的数在后面有有两个为1和2,  ,则首位小于3的所有排列组合为

      
第二位是4,则小于 4的数在后面有有两个为1和2。因此
      
第三位是1,则在其之后小于1的数有0个,所以
      
第四位是5,则在其之后小于5的数有1个,为2,所以
      
最后一位就不用计算啦,因为在它之后已经没有数了,所以
    
根据公式:

所以比 34152  小的组合有61个,即34152是排第62。

代码实现

//对前 10 个自然数(0 ~ 9)的阶乘存入表
const int fact[10] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
int cantor(vector<int> vec){//cantor展开,n表示是n位的全排列,a[]表示全排列的数(用数组表示)
    int n= vec.size();
    int ans=0,sum=0;
    for(int i=0;i<n;i++){
        for(int j=i+1;j<n;j++)
            if(vec[j]<vec[i])
                sum++;
        ans+=sum*fact[n-i-1];//累积
        sum=0;//计数器归零
    }
    return ans+1;
}

逆康托展开举例

一开始已经提过了,康托展开是一个全排列到一个自然数的双射,因此是可逆的。即对于上述例子,在
给出61可以算出起排列组合为34152。由上述的计算过程可以容易的逆推回来,具体过程如下:
用 61 / 4! = 2余13,说明比首位小的数有2个,所以首位为3。
  
用 13 / 3! = 2余1,说明在第二位之后小于第二位的数有2个,所以第二位为4。
  
用 1 / 2! = 0余1,说明在第三位之后没有小于第三位的数,所以第三位为1。
  
用 1 / 1! = 1余0,说明在第四位之后小于第四位的数有1个,所以第四位为5。
  
最后一位自然就是剩下的数2。
通过以上分析,所求排列组合为 34152。

代码实现

//对前 10 个自然数(0 ~ 9)的阶乘存入表
const int fact[10] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
vector<int> revContor(int bits, int num) {
    num = num - 1; //有 num - 1 个排列比目标序列要小
    vector<bool> vis(bits + 1, false);
    vector<int> permutation(bits, -1);
    int n, residue = num;
    for (int i = 0; i < bits; ++i) {
        n = residue / (fact[bits - i - 1]);
        residue = residue % (fact[bits - i - 1]);
        for (int j = 1; j <= bits; ++j) {
            if (!vis[j] && !(n--)) {
                vis[j] = true;
                permutation[i] = j;
                break;
            }
        }
    }
    return permutation;
}

因上求缘,果上努力~~~~ 作者:每天卷学习,转载请注明原文链接:https://www.cnblogs.com/BlairGrowing/p/14043427.html

原文地址:https://www.cnblogs.com/BlairGrowing/p/14043427.html