康拓展開及其逆運算和全排列函式

2021-07-26 02:47:04 字數 3033 閱讀 3630

有所摘抄,但重要的是自己的想法。

康托展開是乙個全排列到乙個自然數的雙射,常用於構建雜湊表時的空間壓縮。 康托展開的實質是計算當前排列在所有由小到大全排列中的順序,因此是可逆的。

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

index:     1     2     ...          n-i+1   ...        n

其中,a[i]為整數,並且0<=a[i]

a[i] 是:假設 一串數字中第index個元素為e,   a[i]表示第index個數e之後的數比e小的數的個數

x表示序列是全排列中第幾個排列(從0開始)。

舉個例子:

原序列: 1 2 3 4 5 6 7 8

求: 3 5 7 4 1 2 9 6 8

x = 2*8! + 3*7! + 4*6! + 2*5! + 0*4! + 0*3! + 2*2! + 0*1! + 0*0! = 98884.

解釋:

排列的第一位是3,比3小的數有兩個,以這樣的數開始的排列有8!個,因此第一項為2*8!

排列的第二位是5,比5小的數有1、2、3、4,由於3已經出現,因此共有3個比5小的數,這樣的排列有7!個,因此第二項為3*7!

以此類推,直至0*0!

用途: 

顯然,n位(0~n-1)全排列後,其康托展開唯一且最大約為n!,因此可以由更小的空間來儲存這些排列。由公式可將x逆推出對應的全排列。

總之就是用來壓縮空間的。。本人也不大懂。

例題:

描述 現在有"abcdefghijkl」12個字元,將其所有的排列中按字典序排列,給出任意一種排列,說出這個排列在所有的排列中是第幾小的?

輸入每行輸入一行字串,保證是a~l這12個字元的某種排列

eof結束 輸出

輸出乙個整數,代表這個排列排在第幾位

樣例輸入1

abcdefghijkl

abcdefghiklj

gfkedhjblcia

樣例輸出1

1

4260726926

#include #include #define ll long long

using namespace std;

int factorial(int l)

int main()

printf("%lld\n", ans);

} return 0;

}

另外,康托展開的逆運算,即通過知道它是第幾個全排列求全排列

例1 的全排列,並且已經從小到大排序完畢

(1)找出第96個數

首先用96-1得到95

用95去除4! 得到3餘23

有3個數比它小的數是4

所以第一位是4

用23去除3! 得到3餘5

有3個數比它小的數是4但4已經在之前出現過了所以第二位是5(4在之前出現過,所以實際比5小的數是3個)

用5去除2!得到2餘1

有2個數比它小的數是3,第三位是3

用1去除1!得到1餘0

有1個數比它小的數是2,第二位是2

最後乙個數只能是1

所以這個數是45321

(2)找出第16個數

首先用16-1得到15

用15去除4!得到0餘15

用15去除3!得到2餘3

用3去除2!得到1餘1

用1去除1!得到1餘0

有0個數比它小的數是1

有2個數比它小的數是3 但由於1已經在之前出現過了所以是4(因為1在之前出現過了所以實際比4小的數是2)

有1個數比它小的數是2 但由於1已經在之前出現過了所以是3(因為1在之前出現過了所以實際比3小的數是1)

有1個數比它小得數是2 但由於1,3,4已經在之前出現過了所以是5(因為1,3,4在之前出現過了所以實際比5小的數是1)

最後乙個數只能是2

所以這個數是14352

最後,還有乙個全排列函式 :next_permutation函式

這是乙個求乙個排序的下乙個排列的函式,可以遍歷全排列,要包含標頭檔案

與之完全相反的函式還有prev_permutation

但做本題是會超時(不用想也是,乙個乙個求肯定tle)

該函式可以對很多態別的陣列序列進行求下乙個字典序排列,即按全排的順序求下乙個排序序列

這裡也貼一下本題的tle**(嘿嘿)

//tle

#include #include #include #include using namespace std;

int main()

res = 1;

while(next_permutation(a, a+12))

} }return 0;

}

此函式各種型別的用法詳解:

2017/4/1日增: 1.

//康拓**實現

#include using namespace std;

int factor(int k)

int main()

cout << ans+1 << endl; //最小序列ans為0,同理ans+1;

} return 0;

}

2.

//逆康拓**實現

#include using namespace std;

int factor(int k)

int ans[105], book[105];

int main()

ans[i] = j-1; //當找到該數時j多加了一次1

book[j-1] = 1;

} for(int i = 1; i <= m; ++i)

printf("%d", ans[i]);

printf("\n");

} return 0;

}

康托展開及其逆運算

康托展開的wiki介紹 申明 1.用c語言實現。2.for中的i,j定義適用於c99標準,gcc編譯要新增 std c99選項。或者將i,j的定義放到for之前的外部作用域。3.逆運算使用了c99標準的vla,即變長陣列,只能用於區域性作用域,且宣告時不能初始化 也可以不用vla,使用malloc等...

總結 康托展開及其逆運算

這裡先貼一道例題 我們先科普一下康托展開 x an n 1 an 1 n 2 ai i 1 a2 1 a1 0 ai為整數,並且0 ai 簡單點說就是,判斷這個數在其各個數字全排列中從小到大排第幾位。比如 1 3 2,在1 2 3的全排列中排第2位。維基 n位 0 n 1 全排列後,其康托展開唯一且...

模板 康拓展開和他的逆運算

康托展開 康托展開的公式是 x an n 1 an 1 n 2 ai i 1 a2 1 a1 0 其中,ai為當前未出現的元素中是排在第幾個 從0開始 n指的是陣列的長度。舉個例子,有乙個陣列 s a b c d 它的乙個排列 s1 d b a c 則 x s1 a4 3 a3 2 a2 1 a1 ...