康托展开

Posted lifehappy

tags:

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

概述

康托展开是一个全排列到一个自然数的双射。设有n个数(1,2,3,4,…,n),可以有组成不同(n!种)的排列组合,康托展开表示的就是是当前排列组合在n个不同元素的全排列中的名次。

康托展开

公式

(X = a[n] * (n - 1)! + a[n - 1] * (n - 2)! + a[n - 2] * (n - 3)! …… + a[2] * 1! + a[1] * 0!)

例子

例如3的全排列

  • 123 (0 * 2! + 0 * 1! + 0 * 0! = 0)
  • 132 (0 * 2! + 1 * 1! + 0 * 0! = 1)
  • 213 (1 * 2! + 0 * 1! + 0 * 0! = 2)
  • 231 (1 * 2! + 1 * 1! + 0 * 0! = 3)
  • 312 (2 * 2! + 0 * 0! + 0 * 0! = 4)
  • 321 (2 * 2! + 1 * 1! + 0 * 0! = 5)

样例解释

估计大家应该不用解释都应该明白了,这里的a[i]系数是如何来的了

  • 321 (2 * 2! + 1 * 1! + 0 * 0!)

3 > 2, 3 > 1,得到a[3] = 2,2 > 1,得到a[2] = 1
其中的康托展开的数值代表的是当前项在全排类中的数值下标(注意下标从0开始计数)

康托展开代码

/*
	Code by lifehappy 2020:04:21
	康托展开计算O(n * n)
	未优化,明天把优化的代码补上。
*/
#include<bits/stdc++.h>
using namespace std;
int fac[10] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};//阶乘
int cantor(int a[], int n) {
    int s = 0;
    for(int i = 0; i < n; i++) {
	int num = 0;
	for(int j = i + 1; j < n; j++)
	    if(a[i] > a[j])
		num++;
	s += num * fac[n - i - 1];
    }
    return s;
}
int main() {
    int a[5] = {4, 5, 3, 1, 2}, n = 5;
    printf("%d
", cantor(a, n));
    return 0;
}
94

逆康托展开

也就是康托展开的逆过程,就拿上面的例子来说

排列4 5 3 1 2的康托展开值是94。

逆运算计算过程

  • 94 / 4! = 3 余 22,从(1 2 3 4 5)中得到第四大的为当前位的数字(4)
  • 22 / 3! = 3 余 4,从(1 2 3 5) 中得到第四大的为当前位的数字(5)
  • 4 / 2! = 2 余 0,从(1, 2, 3) 中得到第三大的为当前位的数字(3)
  • 0 / 1!= 0 余 0,从(1, 2) 中得到第二大的为当前位的数字(1)
  • 直到剩下最后一位

逆运算代码

/*
	Code by lifehappy 2020:04:21
	逆康托展开计算O(n * n)
	康托展开项未优化
*/
#include<bits/stdc++.h>
using namespace std;
int fac[10] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};//阶乘
int ans[10];
int cantor(int a[], int n) {
    int s = 0;
    for(int i = 0; i < n; i++) {
	int num = 0;
	for(int j = i + 1; j < n; j++)	
       	    if(a[i] > a[j])
		num++;
	s += (num * fac[n - i - 1]);
    }
    return s;
}
void decantor(int s, int n) {
    vector<int> a;
    for(int i = 1; i <= n; i++)	a.push_back(i);
    for(int i = 4; i >= 0; i--) {
	int pos = s / fac[i];
	s %= fac[i];
	ans[n - i - 1] = a[pos];
	a.erase(a.begin() + pos); 
    }
}
int main() {
    int a[5] = {4, 5, 3, 1, 2}, n = 5;
    printf("%d
", cantor(a, n));
    decantor(cantor(a, n), n);
    for(int i = 0; i < n; i++)
	printf("%d%c", ans[i], i + 1 == n ? ‘
‘ : ‘ ‘);
    return 0;
}

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

全排列的编码与解码——康托展开 (附完整代码)

康托展开 / 逆康托展开

康托展开 & 逆康托展开

康托展开小结-

LG5367 「模板」康托展开 康托展开

[学习][Math]康托展开和逆康托展开