简体   繁体   English

筛检问题的筛子:处理非常大的数字

[英]Sieve of Eratosthenes problem: handling really big numbers

I'm solving Sphere's Online Judge Prime Generator using the Sieve of Eratosthenes. 我正在使用Eratosthenes筛子解决Sphere的Online Judge Prime Generator

My code works for the test case provided. 我的代码适用于提供的测试用例。 But.. as the problem clearly states: 但是..正如问题明确指出的那样:

The input begins with the number t of test cases in a single line (t<=10). 输入以一行中的测试用例数t开始(t <= 10)。 In each of the next t lines there are two numbers m and n ( 1 <= m <= n <= 1000000000, nm<=100000) separated by a space. 在接下来的每条t行中,有两个数字m和n( 1 <= m <= n <= 1000000000,nm <= 100000)用空格隔开。

I know that the method Integer.parseInt() throws an Exception when handling really big numbers and the online judge was indicating that an Exception was being thrown, so I changed every case of parseInt to parseLong in my code. 我知道Integer.parseInt()方法在处理非常大的数字时会引发Exception,并且在线判断表明正在引发Exception,因此我在代码中将parseInt每种情况都更改为parseLong

Well, the thing is running fine on Netbeans 6.5 with small values for m and n. 好的,在Netbeans 6.5上,m和n的值很小,一切运行良好。

package sphere;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main{

public static void runEratosthenesSieve(long lowerBound, long upperBound) {

      long upperBoundSquareRoot = (long) Math.sqrt(upperBound);

      boolean[] isComposite = new boolean[(int)upperBound + 1];

      for (int m = 2 /*int m = lowerBound*/; m <= upperBoundSquareRoot; m++) {

            if (!isComposite[m]) {

                if (m>=lowerBound) {System.out.println(m);}

                  for (int k = m * m; k <= upperBound; k += m)

                        isComposite[k] = true;

            }

      }

      for (int m = (int)upperBoundSquareRoot; m <= upperBound; m++)

            if (!isComposite[m])

                 if (m>=lowerBound){ System.out.println(m);}

}

public static void main(String args[]) throws java.lang.Exception{

       BufferedReader r = new BufferedReader(new InputStreamReader(System.in));


       String l = r.readLine();

       int testCases = Integer.parseInt(l); 

       for (int i =0; i<testCases; i++){
       String s =r.readLine();

       String []splitted=s.split(" ");


       long lowerBound = Long.parseLong (splitted[0]);
       long upperBound = Long.parseLong(splitted[1]);

       runEratosthenesSieve (lowerBound,upperBound);

       System.out.println("");
       }
}

}

Input+Output: 输入+输出:

run:
2
1 10
2
3
3
5
7

3 5
3
5

BUILD SUCCESSFUL (total time: 11 seconds)

But JCreator LE is saying this: 但是JCreator LE这么说:

2
1 10
Exception in thread "main" java.lang.NumberFormatException: For input string: ""
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
    at java.lang.Long.parseLong(Long.java:424)
    at java.lang.Long.parseLong(Long.java:461)
    at sphere.Main.main(Main.java:51)

Process completed.

Here I don't have an integer overflow, but why would jcreator complain? 在这里,我没有整数溢出,但是为什么jcreator会抱怨?

Considering the borderline testcase, the program implodes on Netbeans too: 考虑到边界测试用例,该程序也会在Netbeans内爆:

run:
2
999900000 1000000000 
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at sphere.Main.runEratosthenesSieve(Main.java:13)
        at sphere.Main.main(Main.java:55)
Java Result: 1

How can I deal with those huge-ish integers of the problem statement? 我该如何处理问题陈述中那些庞大的整数?

Edit: By suggestion I have changed the boolean array for a BitSet, but I'm still getting an OutOFMemoryError : 编辑:通过建议我已更改为BitSet的布尔数组,但我仍然得到OutOFMemoryError

package sphere;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.BitSet;

public class Main{

public static void runEratosthenesSieve(long lowerBound, long upperBound) {

      long upperBoundSquareRoot = (long) Math.sqrt(upperBound);

      //boolean[] isComposite = new boolean[(int)upperBound + 1];

      BitSet isComposite = new BitSet((int)upperBound+1);

      for (int m = 2 /*int m = lowerBound*/; m <= upperBoundSquareRoot; m++) {

            if (!isComposite.get(m)) {

                if (m>=lowerBound) {System.out.println(m);}

                  for (int k = m * m; k <= upperBound; k += m)

                        isComposite.set(m);

            }

      }

      for (int m = (int)upperBoundSquareRoot; m <= upperBound; m++)

            if (!isComposite.get(m))

                 if (m>=lowerBound){ System.out.println(m);}

}

public static void main(String args[]) throws java.lang.Exception{

       BufferedReader r = new BufferedReader(new InputStreamReader(System.in));


       String l = r.readLine();

       int testCases = Integer.parseInt(l); 

       for (int i =0; i<testCases; i++){
       String s =r.readLine();

       String []splitted=s.split(" ");


       long lowerBound = Long.parseLong (splitted[0]);
       long upperBound = Long.parseLong(splitted[1]);

       runEratosthenesSieve (lowerBound,upperBound);

       System.out.println("");
       }
}

}

Input-Output: 输入输出:

run:
1
999900000 1000000000
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at java.util.BitSet.initWords(BitSet.java:144)
        at java.util.BitSet.<init>(BitSet.java:139)
        at sphere.Main.runEratosthenesSieve(Main.java:16)
        at sphere.Main.main(Main.java:58)
Java Result: 1
BUILD SUCCESSFUL (total time: 14 seconds)

Here's your problem: 这是您的问题:

boolean[] isComposite = new boolean[(int)upperBound + 1];

This will use a HUGE amount of space since it allocates 4 bytes per boolean in order to allow faster access. 这将占用大量空间,因为它为每个布尔值分配4个字节以允许更快的访问。 Use a java.lang.BitSet to avoid that. 使用java.lang.BitSet可以避免这种情况。

Eventually, your numbers might get too big for long as well and you'll have to use BigInteger. 最终,您的数字可能还会变得太大了,并且您将不得不使用BigInteger。 But at that point, the sieve of Eratosthenes will probably not cut it anymore as well. 但是到那时,Eratosthenes的筛子可能也不会再切割了。

You're using a lot of space to store your booleans. 您正在使用大量空间来存储布尔值。 You might try to squeeze every boolean into one bit. 您可能会尝试将每个布尔值压缩为一位。 And think about it, do you realy need a boolean for every number between lowerbound and upperbound? 并考虑一下,您确实需要为下限和上限之间的每个数字都设置一个布尔值吗? The even numbers for instance are never prime (except for 2), nor are all multiples of 3 (except for 3) etc. This page might give you some good ideas. 例如,偶数绝不是素数(2除外),也不是3的所有倍数(3除外)等。 此页面可能为您提供一些好主意。

There was a small bug in your BitSet implementation. 您的BitSet实现中有一个小错误。 The line: 该行:

                    isComposite.set(m);

should actually be: 实际上应该是:

                    isComposite.set(k);

With that line fixed, the code ran without errors on the test case 999900000 to 1000000000, spitting out 4,832 primes beginning with 999900017 and ending with 999999937. The BitSet used 125 Mbytes of memory, and the method took 17 seconds to run on my 2.2 GHz laptop. 固定该行后,代码在测试用例999900000到1000000000上运行时没有错误,吐出4,832个以999900017开始并以999999937结尾的素数。BitSet使用125 MB的内存,该方法在我的2.2 GHz上运行需要17秒笔记本电脑。

Ae you using the BigInteger class? 您使用BigInteger类吗? Because if no, I highly recommend it here. 因为如果没有,我在这里强烈推荐。 It will deal with the big numbers you are describing. 它将处理您所描述的大数字。 If that is not good enough, then you need to allocate more memory for the JVM to use by doing -Xmx as a command line parameter. 如果那还不够好,那么您需要通过将-Xmx作为命令行参数来分配更多的内存供JVM使用。 There's an example here: 这里有一个例子:

http://www.coderanch.com/t/384456/Java-General-intermediate/java/Increase-JVM-heap-size-eclipse http://www.coderanch.com/t/384456/Java-General-intermediate/java/Increase-JVM-heap-size-eclipse

There is a BigDecimal as well, if you need decimal numbers to be large as well. 如果需要十进制数字也要大,则还有一个BigDecimal。

I had faced similar issues due the limitations on Java Heap Size. 由于Java堆大小的限制,我遇到了类似的问题。 Instead of using high memory Integer, shifting to boolean solved the problem. 与其使用高内存整数,不如使用布尔值来解决问题。 Find the attached code: 找到随附的代码:

public ArrayList<Integer> sieve(int A) {
    boolean prime [] = new boolean[A + 1];
    Arrays.fill(prime, true);
    prime[0] = prime[1] = false;

    for (int i = 2; i <= A; i++) {
        if (!prime[i])
            continue;

        for (long j = 1L * i * i; j <= (long) A; j += i)
            prime[(int) j] = false;
    }

    ArrayList<Integer> res = new ArrayList<>();

    for (int i = 0; i <= A; i++) {
        if (prime[i])
            res.add(i);
    }

    return res;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM