需要解释排列算法的差异

时间:2013-03-06 20:11:45

标签: java algorithm complexity-theory permutation

我目前正在研究一个问题,要求我找到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。

3 个答案:

答案 0 :(得分:0)

在我看来(1)哪个排列是百万分之一完全取决于你使用的顺序,(2)这种排列是递归的成熟问题。我会把它写成递归程序并递增每次迭代的计数。 [那是你的问题吗?我没有真正看到一个问题......]

答案 1 :(得分:0)

关于上述代码的一般事项:

  1. 你应该实现接口而不是具体的类I.e. List<String> array = ...。与您的Set类似。
  2. 数组从索引0开始,你在索引1开始你的计数器。
  3. 最后回答你的问题,有一种蛮力的方式和更优雅的方式,在数学中使用了一些原则。看看这个解释方法的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微秒。

<强>解释

  1. 由于{a1, ..., an}的排列总数为n!,因此我决定需要一个factorials数组。我存储在其中:{0!, ..., 10!}
  2. 我确定了此序列中的数字位置,对于我们的情况(N = 1000000),它位于9!10!之间。如果它低于9!,我会从fixedDigits数组中添加remainedDigits个数字的填充。
  3. 因为数字大于9!,我计算从数字中提取9!的次数,结果可以帮助我获得第一个数字。然后,我对8!7!
  4. 采用了类似的方法

    以上说明基于以下简单观察。如果我们有一组{a1,...,ai,...,an}并修复了a1,...,ai,我们就可以获得(n-i)!个不同的字符串。


    请注意,如果您使用:

    static List<Integer> remainedDigits = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
    

    您无法从列表中删除元素。 `