我目前正在研究一个问题,要求我找到0,1,2,3,4,5,6,7,8,9的第100万个字典排列。乍一看,我想到了一个非常粗糙的解决方案,其复杂性大约为 O(n ^ 3)
public static String permute(char[] a){
ArrayList<String> array = new ArrayList<String>();
int counter = 1;
for (int i = 0; i < a.length; i++){
array[counter] += a[i];
for (int j = 0; j < i; j++){
array[counter] += a[j];
for(int k = a.length; k > i; k--){
array[counter] += a[k];}counter++;}
}
}
代码可能不完美,但想法是选择一个数字然后移动到数组的末尾。第二个数组创建所选数字后面的数字,第三个数组创建数字后面的数字。这看起来像一个可怕的算法,我记得过去的算法,就像这样。
public static HashSet<String> Permute(String toPermute) {
HashSet<String> set = new HashSet<String>();
if (toPermute.length() <= 1 )
set.add(toPermute);
else {
for (int i = 0; i < toPermute.length(); i++ )
for (String s: Permute(toPermute.substring(0,i)+ toPermute.substring(i+1)))
{
set.add(toPermute.substring(i,i+1)+s);}
}
return set;
}
}
问题是这个算法使用了无序集合,我不知道它如何能够有足够的订单来找到百万个排列。我也不知道除了 O(n ^ 2)这一事实之外的复杂性,因为它自称为 n 次,然后是unstacks。
答案 0 :(得分:0)
在我看来(1)哪个排列是百万分之一完全取决于你使用的顺序,(2)这种排列是递归的成熟问题。我会把它写成递归程序并递增每次迭代的计数。 [那是你的问题吗?我没有真正看到一个问题......]
答案 1 :(得分:0)
关于上述代码的一般事项:
List<String> array = ...
。与您的Set
类似。最后回答你的问题,有一种蛮力的方式和更优雅的方式,在数学中使用了一些原则。看看这个解释方法的site。
答案 2 :(得分:0)
这是一个更有效的解决方案:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class P24 {
static final int digits[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
static List<Integer> remainedDigits = new ArrayList(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
static final int factorials[] = new int[digits.length + 1];
static final int N = 1000_000;
static int low = -1;
static int lowIndex = -1;
static int highIndex = -1;
public static void main(String args[]) {
populateFactorials(digits.length);
validateN(N);
identifyMargins();
int n = N; // it will be changed
int fixedDigits = digits.length - highIndex;
String result = "";
for (int i = 0; i < fixedDigits; i++) {
result += remainedDigits.get(0);
remainedDigits.remove(0);
}
for (int i = fixedDigits; i < digits.length; i++) {
int pos = 0;
int firstDigit = remainedDigits.get(pos);
low = factorials[lowIndex];
while (n - low > 0) {
pos++;
n -= low;
}
lowIndex--;
result += remainedDigits.get(pos);
remainedDigits.remove(pos);
}
System.out.println(result);
}
private static void validateN(int n) {
if (n < 0 || n > factorials[factorials.length - 1]) {
System.out.println("The input number is not valid");
System.exit(0);
}
}
private static void identifyMargins() {
for (int i = 0; i < factorials.length - 1; i++) {
if (factorials[i] <= N && N < factorials[i + 1]) {
lowIndex = i;
highIndex = i + 1;
}
}
}
private static void populateFactorials(int max) {
for (int i = 0; i <= max; i++) {
factorials[i] = fact(i);
}
}
private static int fact(int x) {
if (x == 0 || x == 1) {
return 1;
}
int p = 1;
for (int i = 2; i <= x; i++) {
p *= i;
}
return p;
}
}
时间:305微秒。
<强>解释强>
{a1, ..., an}
的排列总数为n!
,因此我决定需要一个factorials
数组。我存储在其中:{0!, ..., 10!}
。9!
和10!
之间。如果它低于9!
,我会从fixedDigits
数组中添加remainedDigits
个数字的填充。9!
,我计算从数字中提取9!
的次数,结果可以帮助我获得第一个数字。然后,我对8!
,7!
等以上说明基于以下简单观察。如果我们有一组{a1,...,ai,...,an}
并修复了a1
,...,ai
,我们就可以获得(n-i)!
个不同的字符串。
请注意,如果您使用:
static List<Integer> remainedDigits = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
您无法从列表中删除元素。 `