题目是这样的:
标题: 排列序数
X星系的某次考古活动发现了史前智能痕迹。
这是一些用来计数的符号,经过分析它的计数规律如下:
(为了表示方便,我们把这些奇怪的符号用a~q代替)
abcdefghijklmnopq 表示0
abcdefghijklmnoqp 表示1
abcdefghijklmnpoq 表示2
abcdefghijklmnpqo 表示3
abcdefghijklmnqop 表示4
abcdefghijklmnqpo 表示5
abcdefghijklmonpq 表示6
abcdefghijklmonqp 表示7
.....
问题一:
在一处石头上刻的符号是:
bckfqlajhemgiodnp
请你计算出它表示的数字是多少?
问题二:
求第22952601027516个排列的符号排列是?
先学习康托展开式:转自http://www.cnblogs.com/1-2-3/archive/2011/04/25/generate-permutation-part2.html
康托展开的公式是 X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0! 其中,ai为当前未出现的元素中是排在第几个(从0开始)。
这个公式可能看着让人头大,最好举个例子来说明一下。例如,有一个数组 s = ["A", "B", "C", "D"],它的一个排列 s1 = ["D", "B", "A", "C"],现在要把 s1 映射成 X。n 指的是数组的长度,也就是4,所以
X(s1) = a4*3! + a3*2! + a2*1! + a1*0!
关键问题是 a4、a3、a2 和 a1 等于啥?
a4 = "D" 这个元素在子数组 ["D", "B", "A", "C"] 中是第几大的元素。"A"是第0大的元素,"B"是第1大的元素,"C" 是第2大的元素,"D"是第3大的元素,所以 a4 = 3。
a3 = "B" 这个元素在子数组 ["B", "A", "C"] 中是第几大的元素。"A"是第0大的元素,"B"是第1大的元素,"C" 是第2大的元素,所以 a3 = 1。
a2 = "A" 这个元素在子数组 ["A", "C"] 中是第几大的元素。"A"是第0大的元素,"C"是第1大的元素,所以 a2 = 0。
a1 = "C" 这个元素在子数组 ["C"] 中是第几大的元素。"C" 是第0大的元素,所以 a1 = 0。(因为子数组只有1个元素,所以a1总是为
所以,X(s1) = 3*3! + 1*2! + 0*1! + 0*0! = 20
通过康托逆展开生成全排列
如果已知 s = ["A", "B", "C", "D"],X(s1) = 20,能否推出 s1 = ["D", "B", "A", "C"] 呢?
因为已知 X(s1) = a4*3! + a3*2! + a2*1! + a1*0! = 20,所以问题变成由 20 能否唯一地映射出一组 a4、a3、a2、a1?如果不考虑 ai 的取值范围,有
3*3! + 1*2! + 0*1! + 0*0! = 20
2*3! + 4*2! + 0*1! + 0*0! = 20
1*3! + 7*2! + 0*1! + 0*0! = 20
0*3! + 10*2! + 0*1! + 0*0! = 20
0*3! + 0*2! + 20*1! + 0*0! = 20
等等。但是满足 0 <= ai <= n-1 的只有第一组。可以使用辗转相除的方法得到 ai,如下图所示:
知道了a4、a3、a2、a1的值,就可以知道s1[0] 是子数组["A", "B", "C", "D"]中第3大的元素 "D",s1[1] 是子数组 ["A", "B", "C"] 中第1大的元素"B",s1[2] 是子数组 ["A", "C"] 中第0大的元素"A",s[3] 是子数组 ["C"] 中第0大的元素"C",所以s1 = ["D", "B", "A", "C"]。
import java.util.ArrayList;
import java.util.Iterator;
public class Main6 {
public static void main(String[] args) {
// TODO Auto-generated method stub
/* abcdefghijklmnopq 表示0
abcdefghijklmnoqp 表示1
abcdefghijklmnpoq 表示2
abcdefghijklmnpqo 表示3
* 求bckfqlajhemgiodnp代表的数字
* */
long a[] = new long[17];
GetFactorial(a);
long sum = Getansnum("bckfqlajhemgiodnp",a);
System.out.println(sum);
/* abcdefghijklmnopq 表示0
abcdefghijklmnoqp 表示1
abcdefghijklmnpoq 表示2
abcdefghijklmnpqo 表示3
求第22952601027516个排列的符号排列是?
* */
long num = 22952601027516L;
String ans = Getanssign(a,num);
System.out.println(ans);
}
private static String Getanssign(long[] a, long num) {
ArrayList<Character> sub = new ArrayList<>();
ArrayList<Character> result = new ArrayList<>();
for (int i = 0; i <=16; i++) {
sub.add((char) ('a'+i));//生产a-q升序排列;
}
for(int i = 16;i>=0;i--){
int ai;
if (i==0) {
ai = 0;
}else {
ai = (int) (num/a[i]);
}
char temp = sub.get(ai);
result.add(temp);
sub.remove(sub.indexOf(temp));
if (i==0) {
num = 0;
}else {
num = num%a[i];
}
}
Iterator<Character> ansstring = result.iterator();
StringBuffer answer = new StringBuffer();
while(ansstring.hasNext()){
answer.append(ansstring.next());
}
return answer.toString();
}
/**
* @param s 已知的符号序列
* @param a 阶乘数组
* @return s符号序列的是第几个排列
*/
private static long Getansnum(String s, long[] a) {
// TODO Auto-generated method stub
long sum = 0;
char temp[] = s.toCharArray();
int num[] = new int[s.length()];
for (int i = 0; i < temp.length; i++) {
num[16-i] = temp[i];
}
for (int i = 16; i >=0; i--) {
sum = sum + a[i]*getan(num,i);
}
return sum;
}
/**
* @param a 数组a储存阶乘
*/
private static void GetFactorial(long[] a) {
// TODO Auto-generated method stub
for (int i = 0; i < a.length; i++) {
if (i==0){
a[0] = 0;
}else if (i==1) {
a[1] = 1;
}else {
a[i] = a[i-1]*i;
}
}
}
/**
* @param num 输入当前的数组
* @param i
* @return 返回i是第几大的数
*/
private static long getan(int[] num, int i) {
// TODO Auto-generated method stub
int n = 0 ;
for(int j = i; j>=0; j--){
if(num[i]>num[j]) n++;
}
return n;
}
}