为什么迭代置换生成器比递归慢?

时间:2013-11-27 02:45:19

标签: java string recursion permutation

我正在比较两个函数作为我的置换生成器。这个问题涉及很多事情:字符串实习生表,使用迭代与递归解决此问题的利弊等等......

public static List<String> permute1(String input) {
    LinkedList<StringBuilder> permutations = new LinkedList<StringBuilder>();
    permutations.add(new StringBuilder(""+input.charAt(0)));

    for(int i = 1; i < input.length(); i++) {
        char c = input.charAt(i);
        int size = permutations.size();
        for(int k = 0; k < size ; k++) {
            StringBuilder permutation = permutations.removeFirst(),
                    next;
            for(int j = 0; j < permutation.length(); j++) {
                    next = new StringBuilder();
                    for(int b = 0; b < permutation.length(); next.append(permutation.charAt(b++)));
                    next.insert(j, c);
                    permutations.addLast(next);
            }
            permutation.append(c);
            permutations.addLast(permutation);
        }
    }
    List<String> formattedPermutations = new LinkedList<String>();
    for(int i = 0; i < permutations.size(); formattedPermutations.add(permutations.get(i++).toString()));
    return formattedPermutations;
}

public static List<String> permute2(String str) { 
    return permute2("", str); 
}

private static List<String> permute2(String prefix, String str) {
    int n = str.length();
    List<String> permutations = new LinkedList<String>();
    if (n == 0) permutations.add(prefix);
    else 
        for (int i = 0; i < n; i++) 
            permutations.addAll(permute2(prefix + str.charAt(i), str.substring(0, i) + str.substring(i+1, n)));
    return permutations;
}

我认为这两种算法通常应该相等,但是递归实现最好能达到n = 10,而permute1,即交互式解决方案,在n = 8时有一个outofmemoryerror,其中n是输入字符串长度。事实上我正在使用StringBuilder然后转换为Strings一个坏主意吗?如果是这样,为什么?我想每当你添加一个字符串时它会创建一个新字符串,这会很糟糕,因为那时java会实习它,对吧?所以你最终得到了一堆中间字符串,这些字符串不是排列,而是插在实习表中。

编辑:

我用String替换了StringBuilder,这消除了使用StringBuilder.insert()的需要。但是,我必须使用String.substring()来构建排列字符串,这可能不是最好的方法,但它在经验上比StringBuilder.insert()更好。我没有像Alex Suo建议的那样使用char数组,因为我的方法应该返回一个字符串列表,我必须将这些char数组转换为字符串,这会导致char数组上更多的垃圾收集(OutOfMemoryError的原因) )。因此,有了这个,OutOfMemoryError和慢度问题都得到了解决。

public static List<String> permute3(String input) {
        LinkedList<String> permutations = new LinkedList<String>();
        permutations.add(""+input.charAt(0));
        for(int i = 1; i < input.length(); i++) {
            char c = input.charAt(i);
            int size = permutations.size();
            for(int k = 0; k < size ; k++) {
                String permutation = permutations.removeFirst(),
                        next;
                for(int j = 0; j < permutation.length(); j++) {
                        next = permutation.substring(0, j + 1) + c + permutation.substring(j + 1, permutation.length());
                        permutations.addLast(next);
                }
                permutations.addLast(permutation + c);
            }
        }
        return permutations;
    }

1 个答案:

答案 0 :(得分:1)

首先,既然你得到了OutOfMemoryError,那就告诉我你有很多GC正在进行中,众所周知,GC是一个性能杀手。由于年轻的GC已经停止了世界,你可能会因为患有GC而遭受更糟糕的表现。

查看代码,如果深入研究StringBuilder的实际实现,可以看到insert()是一个非常昂贵的操作,涉及System.arraycopy()等,并且可能是expandCapacity()。由于你没有提到你的排列,我假设n <10所以你不会遇到问题 - 你会有内存重新分配,因为StringBuilder的默认缓冲区只有16。 StringBuilder基本上是一个自动char数组,但它并不神奇 - 无论你需要通过从头编写代码来做什么,StringBuilder也需要这样做。

如上所述,如果你真的想要达到最大性能,因为你的字符串数组的长度是预先定义的,为什么不使用长度为String = length(String)的char数组呢?这可能是表现最佳的。