康托展开和逆康托展开

Posted TensionRidden

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了康托展开和逆康托展开相关的知识,希望对你有一定的参考价值。

问题:给定的全排列,计算出它是第几个排列?

  对于全排列,不清楚的可以参考全排列

  方法:康托展开

  对于一个长度为 n 的排列 num[1..n], 其序列号 X 为

  X = a[1]*(n-1)! + a[2]*(n-2)! +...+ a[i]*(n-i)! +...+ a[n-1]*1! + a[n]*0!

  其中a[i]表示在num[i+1..n]中比num[i]小的数的数量

  写做伪代码为:

Cantor(num[])
    X = 0
    For i = 1 .. n
        tp = 0
        For j = i + 1 .. n
            If (num[j] < num[i]) Then
                tp = tp + 1
            End If
        End For
        X = X + tp * (n - i)!
    End For
    Return X

  实现代码为:

// 给定一个全排列, 计算它是第几个排列

#include <iostream>
#include <algorithm>
#include <string>
using namespace std;

// string 转 int 
void strToNum(string s, int num[10]){
    int len = s.length();
    num[0] = len;
    for(int i = 1; i<= len; i++){
        num[i] = s[i-1] - \'0\';
    }
}

// 输出 
void Pri(int num[10]){
    for(int i = 1; i<= num[0]; i++){
        cout << num[i];
    }
    cout << endl;
}

// 阶乘 
int factorial(int n){
    int x = 1;
    for(int i = n; i>=2; i--){
        x = x*i;
    }
    return x;
}

// 康托展开 
int Cantor(int num[10]){
    int X = 0;
    for(int i = 1; i<= num[0]; i++){
        int tp = 0;
        for(int j = i+1; j<= num[0]; j++){
            if(num[j]< num[i]){
                tp++;
            }
        }
        X += tp*factorial(num[0]-i); 
    }
    return X;
}

int main(){
    int num[10];
    string str;
    cin >> str;
    strToNum(str, num);
    Pri(num);
    int X = Cantor(num);
    cout << X << endl;
}
Cantor

 

问题:已知X,如何去反向求解出全排列?

  方法: 逆康托展开
  根据 康托展开的公式,可以推出

  因为    a[i] <=  n-i
       X = a[1]*(n-1)! + a[2]*(n-2)! +...+ a[i]*(n-i)! +...+ a[n-1]*1! + a[n]*0!   所以 a[i]
*(n-i)! <= (n-i)(n-i)! <= (n-i+1)!

  那么也就是说,如果用 X 除以 (n-1)! 得到商 c 和余数 r,其中 c 就等于 a[1], r 等于后面的部分

  写做伪代码为:

unCantor(X):
    a = []
    num = []
    used = [] // 长度为n的boolean数组,初始为false
    For i = 1 .. n
        a[i] = X / (n - i)!
        X = X mod (n - i)!
        cnt = 0
        For j = 1 .. n
            If (used[j]) Then
                cnt = cnt + 1
                If (cnt == a[i] + 1) Then
                    num[i] = j
                    used[j] = true
                    Break
                End If
            End If
        End For
    End For
    Return num

  代码实现为:

// 给定排列元素 num 数组以及排列序号 X,求该排列
// 排列从 0 开始 
#include <iostream>
#include <algorithm>
#include <string>
#define Inf 100000
using namespace std;

// string 转 int 
void strToNum(string s, int num[10]){
    int len = s.length();
    num[0] = len;
    for(int i = 1; i<= len; i++){
        num[i] = s[i-1] - \'0\';
    }
}

// 输出 
void Pri(int num[10]){
    for(int i = 1; i<= num[0]; i++){
        cout << num[i];
    }
    cout << endl;
}

// 阶乘 
int factorial(int n){
    int x = 1;
    for(int i = n; i>=2; i--){
        x = x*i;
    }
    return x;
}

// 逆康托展开
void inverseCantor(int num[10], int n, int out[10]){
    out[0] = num[0];
    for(int i = 1; i<= out[0]; i++){
        int t = factorial(out[0]-i);
        out[i] = num[n/t+1];
        num[n/t+1] = Inf;
        sort(num+1, num+num[0]+1);
        num[0]--;
        n = n%t;
    } 
}

int main(){
    int X, num[10], out[10];
    string str;
    cin >> str;
    cin >> X;
    strToNum(str, num);
    sort(num+1, num+num[0]+1);
    Pri(num);
    // num 数组已排好序 
    inverseCantor(num, X, out); 
    Pri(out);
    return 0;
} 
unCantor

 

 

 

以上是关于康托展开和逆康托展开的主要内容,如果未能解决你的问题,请参考以下文章

康托展开 / 逆康托展开

康托展开 & 逆康托展开

哈希!简康托展开与逆康托展开

数据结构——康托展开与逆康托展开

逆康托展开

hdu1027(逆康托展开)