What inside a total permutation?
首先逐级分析,给每个permutation标上唯一的索引:
Level 1:
index | initiation |
1 | 1 |
2 | 1 |
... | 1 |
(n-1)! | 1 |
(n-1)!+1 | 2 |
(n-1)!+2 | 2 |
... | 2 |
(n-1)!+(n-1) | 2 |
... | ... |
(k-1)(n-1)! | k-1 |
(k-1)(n-1)!+1 | k |
(k-1)(n-1)!+2 | k |
... | k |
(k-1)(n-1)!+(n-1)! | k |
... | ... |
(n-1)(n-1)!+1 | n |
(n-1)(n-1)!+2 | n |
... | n |
n(n-1)! | n |
Level 2:
index | initiation |
(k-1)(n-1)!+1 | k1 |
(k-1)(n-1)!+2 | k1 |
... | k1 |
(k-1)(n-1)!+(n-2)! | k1 |
(k-1)(n-1)!+(n-2)!+1 | k2 |
(k-1)(n-1)!+(n-2)!+2 | k2 |
... | |
(k-1)(n-1)!+(n-2)!+(n-2)! | k2 |
... | ... |
(k-1)(n-1)!+(t-1)(n-2)! | k(t-1) |
(k-1)(n-1)!+(t-1)(n-2)!+1 | kt |
(k-1)(n-1)!+(t-1)(n-2)!+2 | kt |
... | ... |
(k-1)(n-1)!+(t-1)(n-2)!+(n-2)! | kt |
... | ... |
(k-1)(n-1)!+(n-1)(n-2)!+1 | kn |
(k-1)(n-1)!+(n-1)(n-2)!+2 | kn |
... | ... |
(k-1)(n-1)!+n(n-2)! | kn |
Level 3:
index | initiation |
(k-1)(n-1)!+(t-1)(n-2)!+1 | kt1 |
(k-1)(n-1)!+(t-1)(n-2)!+2 | kt1 |
... | kt1 |
(k-1)(n-1)!+(t-1)(n-2)!+(n-3)! | kt1 |
(k-1)(n-1)!+(t-1)(n-2)!+(n-3)!+1 | kt2 |
(k-1)(n-1)!+(t-1)(n-2)!+(n-3)!+2 | kt2 |
... | ... |
(k-1)(n-1)!+(t-1)(n-2)!+2(n-3)! | kt2 |
... | ... |
(k-1)(n-1)!+(t-1)(n-2)!+(m-1)(n-3)! | kt(m-1) |
(k-1)(n-1)!+(t-1)(n-2)!+(m-1)(n-3)!+1 | ktm |
(k-1)(n-1)!+(t-1)(n-2)!+(m-1)(n-3)!+2 | ktm |
... | ... |
(k-1)(n-1)!+(t-1)(n-2)!+m(n-3)! | ktm |
... | ... |
(k-1)(n-1)!+(t-1)(n-2)!+(n-1)(n-3)!+1 | ktn |
(k-1)(n-1)!+(t-1)(n-2)!+(n-1)(n-3)!+2 | ktn |
... | ... |
(k-1)(n-1)!+(t-1)(n-2)!+n(n-3)! | ktn |
假设我们把n个数存放在数组F[n]中(初始化F[i]=i).
可见索引为K的n位的permutation所对应的第一位是F[ceil(K/(n-1)!)]
纪录下这一位以后我们进入二级子表,在子表中的索引和原索引存在类似求模的关系:
K'= K%(n-1)!==0?(n-1)!: K%(n-1)!
而对应的F' = F.remove(ceil(K/(n-1)!))
接下来重复第一步:可见索引为K的n-1位的permutation所对应的第一位是F'[ceil(K'/(n-2)!)]
按照此过程归纳到F为空,此时K permutation所有位数值已完全纪录。
Solution:
public static String getPermutation(int n, int k) {
ArrayList<Integer> f = new ArrayList<Integer>();
for(int i=0;i<n+1;i++) f.add(i);
String ret="";
int m = k;
for(int lv = 1; lv<=n; lv++){
int b = factorial(n-lv);
int a = (int) Math.ceil(m*1.0/b);
ret += f.get(a);
f.remove(a);
if(lv<n){
m = m%b==0?b:m%b;
}
}
return ret;
}
What inside a next permutation?
如果序列{a1a2a3...an}已经是非严格降序(字典序),那么这个序列就是最大的序列了。
假如再任给一个序列X={x1x2...xm},将这个序列附在X后面,即Xa1a2a3...an. 令X'={x1x2...x_m-1}, 那么可以得知这个序列是以X'xm所引导的最大序列。
根据字典序的定义,可知对于任意序列{a1a2a3...an}:
X'sort(xm,a1,a2,a3,...,an)<X'xm sort(a1,a2,a3,...,an)<X'xm,a1,a2,a3,...,an<X'xm reverse(sort(a1,a2,a3,...,an))<X'reverse(sort(xm,a1,a2,a3,...,an))
那么,如果{xm,a1,a2,a3,...,an}是非单调的,则X'xm,a1,a2,a3,...,an的下一个序列一定是处于X'sort(xm,a1,a2,a3,...,an) 和 X'reverse(sort(xm,a1,a2,a3,...,an))之间的(包括边界)。因此一切xm之前的序列都可以不予以考虑!
现在分析xm应该和{a1a2a3...an}中的谁置换以确定下一序列的开头元素。首先可以发现,与xm置换的元素不得小于等于xm,否则置换完成以后序列排名无法提升。那么,在所有大于xm的元素中,我们一定是要选择最接近xm的。这样完成的置换可以保证是增幅最小的。
当置换完成后,我们有序列X'ai,a1,a2,a3,...,xm,...,an, 而同样的,对于这个由X' ai所引导的序列存在上下限:
X'ai sort(a1,a2,a3,...,xm,...,an)<X'ai,a1,a2,a3,...,xm,...,an<X'ai reverse(sort(a1,a2,a3,...,xm,...,an))
由之前的分析知:
xm<ai
X'xm,a1,a2,a3,...,an<X'xm reverse(sort(a1,a2,a3,...,an))<X'ai sort(a1,a2,a3,...,xm,...,an)<X'ai,a1,a2,a3,...,xm,...,an
Solution:
public static void nextPermutation(int[] num) {
for(int i=num.length-1;i>0;i--){
if(num[i]>num[i-1]){
for(int j=num.length-1;j>=i;j--){
if(num[i-1]<num[j]){
int t = num[j];
num[j] = num[i-1];
num[i-1] = t;
/**
* a - the array to be sorted
* fromIndex - the index of the first element, inclusive, to be sorted
* toIndex - the index of the last element, exclusive, to be sorted
*/
Arrays.sort(num,i,num.length);
return;
}
}
}
}
Arrays.sort(num);
}