基于这篇介绍我实现了基于Java的算法
都能看懂的康托展开_ltrbless的博客-优快云博客blog.youkuaiyun.com
条件
一个数组:[1, 2, 3, 4, 5]
康托展开算法
找出 52413 是这个数组的全排列中的第几个
算法详解:
5 作为第一个数,比它小的数有四个,5 的后面有4个位置可待排序使用,则这四个数的全排列有 4 * 4! 种
2 作为第二个数,比它小的数只有一个,2 后面只有3个位置可用来做全排列使用,全排列的个数是 1 * 3!
4 作为第三个数,比它小的数有3个,但是2已经使用过了,所以只能用2个,全排列的个数是 2 * 2!
同理可得 1 有 0 * 1!种,3 有 0 * 0!种
把上面提到的全排列的种数相加就得到了 106
因为这种算法是从 0 开始计算的,所以 52413 是第 106+1 个排列Java源码
/**
* 输入一个整数,返回它是该数组全排列里面的第几个数
*
* @param n
* @return
*/
public static int cantorExpansion(int n) {
int position = 0;
String number = String.valueOf(n);
for (int i = 0; i < number.length(); i++) {
position += (getMinCount(number.charAt(i), number, i) * getFactorial(number.length()-i-1));
}
return position + 1;
}
// 求小于等于 x 的数有几个,且排除已经处理过的数
public static int getMinCount(char x, String number, int index) {
Integer num = Integer.valueOf(String.valueOf(x));
for (int i = 0; i < index; i++) {
Integer tmp = Integer.valueOf(String.valueOf(number.charAt(i)));
if (tmp < num) {
num--;
}
}
return num - 1;
}
// 求x的阶乘
public static int getFactorial(int x) {
if (x == 0)
return 1;
int res = x;
for (int i = 1; i < x; i++) {
res *= i;
}
return res;
}
public static void main(String[] args) {
System.out.println(cantorExpansion(52413)); // 107
System.out.println(cantorExpansion(312)); // 5
System.out.println(cantorExpansion(34125)); // 61
}逆康托算法
该数组的全排列中的第 107 个排列是什么数找到
算法详解:n = 5, k = 107
上面提到过,康托展开是从 0 开始计算的,所以求 107 个排列数的时候应该求的是 106
12345 作为第一个排列数,每一个数字可以排列的次数是 4!,3!,2!,1!,0!
用 106 / 24 = 4 ... 10 可以得到第106个排列的第一个数字应该是 array[4] = 5
第一个数找到之后更新 k 为上一次计算的余数,同时把上一次在数组中找到的数删除,重复以上过程
最终得到一个数字序列,该序列就是第 107 个排列对应的数据Java源码
public static void main(String[] args) {
System.out.println(inverseCantorExpansion(5, 107));//52413
System.out.println(inverseCantorExpansion(8, 27));// 12354768
}
/**
* 给出 [1,2,3,...,n] 数组以及该数组全排列的第 K 个序列编号,求第 K 个数是啥
*
* @param n 数组的最大值
* @param k 序号
* @return 第K个数的值
*/
public static String inverseCantorExpansion(int n, int k) {
// 先搞一个1~n的数组,用来在里面选择合适的数字加入 ans 中
ArrayList<Integer> array = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
array.add(i + 1);
}
StringBuilder ans = new StringBuilder();
k -= 1;
// 执行 n 次循环得到 n 个数,组合成一个答案
for (int i = 0; i < n; i++) {
// 计算从 (n-1)! 开始的阶乘
int Xfactorial = getFactorial(n - i - 1);
// 计算获取数组的那一个位置的数字
int index = k / Xfactorial;
// 获取该数字
ans.append(array.get(index));
// 更新 k 为上一次除法计算的余数
k %= Xfactorial;
// 同时更新数组,删除已经获得过的数字
array.remove(index);
}
return ans.toString();
}
// 求x的阶乘
public static int getFactorial(int x) {
if (x == 0)
return 1;
int res = x;
for (int i = 1; i < x; i++) {
res *= i;
}
return res;
}本文算法文字解释很简洁,看不明白的话可以看我开头引用的优快云博客的介绍。
看完他的介绍再看我的就十分清晰了。
本文介绍了如何使用Java实现康托展开和逆康托展开算法,以解决数组全排列问题。通过示例解释算法过程,并提供Java源码进行辅助理解。
1045

被折叠的 条评论
为什么被折叠?



