康托展开和逆康托展开
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; }
问题:已知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; }
以上是关于康托展开和逆康托展开的主要内容,如果未能解决你的问题,请参考以下文章