第k个字典序全排列
2017-04-17 20:00
260 查看
转自:
http://blog.csdn.net/scarecrow398966925/article/details/25543587
给定一个数字n和k,表示求一个由n个数字组成的序列的第k个字典序排列。比如给定n=3,k = 2则求由数字【1,2,3】组成的所有序列中的安字典序排列的第k个。不难看出这三个数字组成的序列的按字典序排序如下:
1,2,3
1,3,2
2,1,3
2,3,1
3,1,2
3,2,1
所以得到的第k=2个序列就是1,3,2。
分析:
可以用上一篇文章所说的NextPermutation方法在初始序列为【1,2,3,···n】的基础上调用k-1次nextPermutation方法即可。
可以利用康托编码来实现。
康托编码:
对于一个集合{1,2,3,4,...,n}的从小到大排序(或者从大到小,与从小到大类似,这里只说前者)的全排列显然它有n!项用自然数1,2,...,n!与之一一对应,这是一种对应法则,康托编码就是这么一种可以实现按字典序排序的序列与自然数之间相互对应的双射。
编码规则为:
对于{1,2,3,...,n}生成的已经从小到大排序好的全排列
x=a
*(n-1)!+a[n-1]*(n-2)!+...a[1]*0!
a[m]代表比在第m位的数字小并且没有在第m位之前出现过的数字的个数(以个位数为第1位)
x代表比这个数小的数的个数,所以这个数的顺序就是x+1
例1 {1,2,3,4,5}的全排列,并且已经从小到大排序完毕,找出45231在这个排列中的顺序
比4小的数有3个
比5小的数有4个,但4已经在之前出现过了所以是3个
比2小的数有1个
比3小的数有两个,但2已经在之前出现过了所以是1个
比1小的数有0个
那么45231在这个排列中的顺序是3*4!+3*3!+1*2!+1*1!+0*0!+1=94
例2 {1,2,3,4,5,6}的全排列,并且已经从小到大排序完毕,找出423615在这个排序中的顺序
比4小的数有3个
比2小的数有1个
比3小的数有2个,但2已经在之前出现过了所以是1个
比6小的数有5个,但4,2,3已经在之前出现过了所以是2个
比1小的数有0个
比5小的数有4个,但1,2,3,4已经在之前出现过了所以是0个
那么423615在这个排序中的顺序是3*5!+1*4!+1*3!+2*2!+0*1!+0*0!+1=395
解码规则为:
解码规则是编码规则的逆运算,即在知道某个序列的排列序数(y)的时候,求这个序列。先对y-1,得到x,然后分别求序列的每个位置的数。举例说明
例1 {1,2,3,4,5}的全排列,并且已经从小到大排序完毕
(1)找出第96个数
首先用96-1得到95
用95去除4! 得到3余23
用23去除3! 得到3余5
用5去除2!得到2余1
用1去除1!得到1余0
有3个数比它小的数是4,所以第一位是4
有3个数比它小的数是4,但4已经在之前出现过了所以是5(因为4在之前出现过了所以实际比5小的数是3个)
有2个数比它小的数是3
有1个数比它小的数是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
通过上面的例子应该足够对康托编码进行解码了,对于本题来说,就是一个解码过程,程序如下::
int factorial(int n){
int res = 1;
for(int i = 1;i<=n;i++)
res *= i;
return res;
}
vector<int> KthPermutation2(int n,int k){
vector<int> vec;
vector<int> res;
for(int i = 0;i<n;i++)
vec.push_back(i+1);
int base = factorial(n-1);
k--;
for(int i = 0;i<n-1;i++){
int divieRes = k/base;
k = k%base;
auto num = next(vec.begin(),divieRes);
res.push_back(*num);
vec.erase(num);
base /= (n-i-1);
}
res.push_back(vec[0]);
return res;
}
http://blog.csdn.net/scarecrow398966925/article/details/25543587
给定一个数字n和k,表示求一个由n个数字组成的序列的第k个字典序排列。比如给定n=3,k = 2则求由数字【1,2,3】组成的所有序列中的安字典序排列的第k个。不难看出这三个数字组成的序列的按字典序排序如下:
1,2,3
1,3,2
2,1,3
2,3,1
3,1,2
3,2,1
所以得到的第k=2个序列就是1,3,2。
分析:
可以用上一篇文章所说的NextPermutation方法在初始序列为【1,2,3,···n】的基础上调用k-1次nextPermutation方法即可。
可以利用康托编码来实现。
康托编码:
对于一个集合{1,2,3,4,...,n}的从小到大排序(或者从大到小,与从小到大类似,这里只说前者)的全排列显然它有n!项用自然数1,2,...,n!与之一一对应,这是一种对应法则,康托编码就是这么一种可以实现按字典序排序的序列与自然数之间相互对应的双射。
编码规则为:
对于{1,2,3,...,n}生成的已经从小到大排序好的全排列
x=a
*(n-1)!+a[n-1]*(n-2)!+...a[1]*0!
a[m]代表比在第m位的数字小并且没有在第m位之前出现过的数字的个数(以个位数为第1位)
x代表比这个数小的数的个数,所以这个数的顺序就是x+1
例1 {1,2,3,4,5}的全排列,并且已经从小到大排序完毕,找出45231在这个排列中的顺序
比4小的数有3个
比5小的数有4个,但4已经在之前出现过了所以是3个
比2小的数有1个
比3小的数有两个,但2已经在之前出现过了所以是1个
比1小的数有0个
那么45231在这个排列中的顺序是3*4!+3*3!+1*2!+1*1!+0*0!+1=94
例2 {1,2,3,4,5,6}的全排列,并且已经从小到大排序完毕,找出423615在这个排序中的顺序
比4小的数有3个
比2小的数有1个
比3小的数有2个,但2已经在之前出现过了所以是1个
比6小的数有5个,但4,2,3已经在之前出现过了所以是2个
比1小的数有0个
比5小的数有4个,但1,2,3,4已经在之前出现过了所以是0个
那么423615在这个排序中的顺序是3*5!+1*4!+1*3!+2*2!+0*1!+0*0!+1=395
解码规则为:
解码规则是编码规则的逆运算,即在知道某个序列的排列序数(y)的时候,求这个序列。先对y-1,得到x,然后分别求序列的每个位置的数。举例说明
例1 {1,2,3,4,5}的全排列,并且已经从小到大排序完毕
(1)找出第96个数
首先用96-1得到95
用95去除4! 得到3余23
用23去除3! 得到3余5
用5去除2!得到2余1
用1去除1!得到1余0
有3个数比它小的数是4,所以第一位是4
有3个数比它小的数是4,但4已经在之前出现过了所以是5(因为4在之前出现过了所以实际比5小的数是3个)
有2个数比它小的数是3
有1个数比它小的数是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
通过上面的例子应该足够对康托编码进行解码了,对于本题来说,就是一个解码过程,程序如下::
int factorial(int n){
int res = 1;
for(int i = 1;i<=n;i++)
res *= i;
return res;
}
vector<int> KthPermutation2(int n,int k){
vector<int> vec;
vector<int> res;
for(int i = 0;i<n;i++)
vec.push_back(i+1);
int base = factorial(n-1);
k--;
for(int i = 0;i<n-1;i++){
int divieRes = k/base;
k = k%base;
auto num = next(vec.begin(),divieRes);
res.push_back(*num);
vec.erase(num);
base /= (n-i-1);
}
res.push_back(vec[0]);
return res;
}
相关文章推荐
- 第k个字典序全排列(kth permutation)
- 第k个字典序全排列(kth permutation)- 康托排列
- 模拟之全排列的第k个字典序
- 全排列与字典序排列
- 全排列(递归和字典序)
- 生成字典序的全排列
- leetcode 60. Permutation Sequence 第K个全排列
- 字典序全排列简单研究
- 字典序全排列
- LeetCode题解-全排列的第k个数字(全排列变体)
- 字典序全排列算法
- poj 1731 字典序全排列
- 输出n的全排列的字典序编号为k的全排列
- 按字典序输出数组的全排列
- 全排列(按字典序)
- 字典序全排列
- 字典序全排列生成算法
- LeetCode | Permutation Sequence(找到全排列中的第k个排列)
- POJ 1833 求下第K个字典序
- POJ 1833 排列(字典序全排列、next_permutation()、copy+流迭代器)