java.lang.OutOfMemoryError:Java堆空间,用于查找长度为n的所有字符串

时间:2011-12-20 17:38:46

标签: java out-of-memory

我想用字母a,b和c生成所有可能的长度为n的字符串。我收到此错误java.lang.OutOfMemoryError:Java堆空间。我的堆大小是512米。你能为我提出替代方案吗?

4 个答案:

答案 0 :(得分:5)

您当前正在计算的字符串数量为

3^30 =                205 891 132 094 649

这是非常多的......

知道每个String包含三个字节:

3^30 * 3 =            617 673 396 283 947

加上二维数组的32位或64位指针。

3^30 * (3 + 4) =    1 441 237 924 662 540  // 32-bit Java VM
3^30 * (3 + 8) =    2 264 802 453 041 140  // 64-bit Java VM

哪个是

2 109 261 GB = 2059 TB     // 64-bit JVM

我想这就是问题所在。


限制为500 MB,您可以解决这个等式:

  3^x * (3 + 8) = 524 288 000
            3^x = 47662545
              x = log(47662545) / log(3)
              x = 7 / 0.477121254719662
              x = 14.67

所以,如果我什么也没有忘记,那么你的测试应该适用于n <= 14。当然,除非删除此代码,否则它将无效:

List<String> result = new ArrayList<String>();
for (char[] permutation : table) {
    result.add(new String(permutation));
}

此代码复制了所有数据!这意味着您需要两倍的内存量。尝试立即打印。

答案 1 :(得分:1)

我在你的评论中指出你有一个不同的问题。你想找到长度为30的所有字符串,a-z不包含a-c。这是长度为30的所有字符串的计数,即d-z。计数是(26-3)^ 30。

System.out.printf("%,d%n", BigInteger.valueOf(26-3).pow(30));

打印

71,094,348,791,151,363,024,389,554,286,420,996,798,449

您可以将每个可能的String编码为数字,而不是记住每个字符串。在您的情况下,您可以使用long

public static void main(String... args) {
    String letters = "abc";
    int len = 30;
    long combinations = (long) Math.pow(letters.length(), len);
    System.out.printf("There are %,d strings%n", combinations);
    for (long i = 0; i < 10; i++)
        System.out.println(fromLong(i, letters, len));
    System.out.println("... some numbers skipped ...");
    for (long i = combinations-10; i < combinations; i++)
        System.out.println(fromLong(i, letters, len));
}

public static String fromLong(long n, String letters, int len) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < len; i++) {
        sb.append(letters.charAt((int) (n % letters.length())));
        n /= letters.length();
    }
    return sb.reverse().toString();
}

打印

There are 205,891,132,094,649 strings
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaab
aaaaaaaaaaaaaaaaaaaaaaaaaaaaac
aaaaaaaaaaaaaaaaaaaaaaaaaaaaba
aaaaaaaaaaaaaaaaaaaaaaaaaaaabb
aaaaaaaaaaaaaaaaaaaaaaaaaaaabc
aaaaaaaaaaaaaaaaaaaaaaaaaaaaca
aaaaaaaaaaaaaaaaaaaaaaaaaaaacb
aaaaaaaaaaaaaaaaaaaaaaaaaaaacc
aaaaaaaaaaaaaaaaaaaaaaaaaaabaa
... some numbers skipped ...
cccccccccccccccccccccccccccbcc
ccccccccccccccccccccccccccccaa
ccccccccccccccccccccccccccccab
ccccccccccccccccccccccccccccac
ccccccccccccccccccccccccccccba
ccccccccccccccccccccccccccccbb
ccccccccccccccccccccccccccccbc
ccccccccccccccccccccccccccccca
cccccccccccccccccccccccccccccb
cccccccccccccccccccccccccccccc

这可以打印从0到3 ^ 30-1的每个可能的字符串。您不需要存储所有编码值,因为您知道所有可能的值都在连续范围内。

答案 2 :(得分:1)

计算不包含子串“ABC”的字符串可以通过动态编程来解决。

首先,让我们将长度为n的所有字符串的编号命名为S(n)。请注意,S(n)很容易计算。 S(n) = pow(size_of_the_alphabet,n)

让我们将包含长度为n的“ABC”的所有字符串的编号命名为A(n),并将第k次位置首次出现“ABC”的所有字符串的编号命名为A(n,k)

现在请注意: A(n,k) = (S(k-1) - A(k-1)) * S(n - k - 3)(因为k是发生“ABC”的第一个位置,这些字符串中的每一个在此位置之前都有一个没有“ABC”的子字​​符串,在第一个“ABC”之后有一个子字符串。)

请注意A(n) = sum A(n,k) for k in [0..n-3]

现在我们可以计算A(n)从0开始计算A(n)的每个值。

基本案例很简单,如A(n) = 0 for n = 0,1,2

A(3) = (S(0) - A(0))*S(0) = 1(即“ABC”)

等。

获得A(n)后,您可以使用公式S(n) - A(n)获取您要查找的号码。

Pseudojava伪代码:

public class Counter {


    public int count(int aSize, int n) {
        long[] a = new long[n+1]; // n + 1 elements since a[i] contains # of strings containing "ABC"
        a[0] = 0;
        a[1] = 0;
        a[2] = 0;


        for (int i = 3; i <= n; ++i){
            long sum = 0;
            for (int k = 0; k <= i-3; ++k) {
                sum += (pow(aSize, k) - a[k]) * pow(aSize, i - k - 3);
            }
            a[i] = sum;
        }
        return a[n];

    }

    public static void main(String... args) {
        int aSize = 3; //size of the alphabet
        int n = 30; // length of the strings

        //final result
        long result = pow(aSize, n) - count(aSize, n);


    }
}    

运行时间为O(n^2),假设pow为O(1)。如果不是那么你可以节省一些预先计算S(i)的时间。

空间要求为O(n)

请注意,这会计算长度为== n的所有字符串的数量。如果你想要长度&lt; = n那么修改是显而易见的(你只需加总a中的所有元素。)

答案 3 :(得分:0)

您是在尝试计算字符串还是计算字符串?

编辑:我从后面的评论中看到你知道你在谈论多少个字符串: 如果你只是计算,这是一个标准的排列封闭形式:26^n = 26提升到n次幂(假设你只使用从'a'到'z'的小写字母)。 /德尔>

如果您真的想要枚举每个字符串,我强烈建议您确保不保留对每个String的引用。如果您最终对这些字符串中的每个字符串悬挂引用,那么在大约六个字符后,您将会耗尽内存。