我在一个编程网站上遇到了以下问题:彼得想为其密码系统生成一些质数。救救他!您的任务是生成两个给定数字之间的所有素数!
输入项
输入以一行中的测试用例数t开始(t <= 10)。在接下来的每条t行中,有两个数字m和n(1 <= m <= n <= 1000000000,nm <= 100000)用空格隔开。
我想出了以下解决方案:
import java.util.*; public class PRIME1 { static int numCases; static int left, right; static boolean[] initSieve = new boolean[32000]; static boolean[] answer; public static void main(String[] args) { Scanner sc = new Scanner(System.in); numCases = sc.nextInt(); initSieve[0] = true; initSieve[1] = true; Sieve(); for (int j = 0; j < numCases; j++) { String line = sc.next(); String line2 = sc.next(); left = Integer.parseInt(line); right = Integer.parseInt(line2); answer = new boolean[right - left + 1]; getAnswer(); for (int i = 0; i < answer.length; i++) { if (!answer[i]) { int ans = i + left; System.out.println(ans); } } System.out.println(); } } public static void Sieve() { for (int i = 2; i < 32000; i++) { if (!initSieve[i]) { for (int j = 2 * i; j < 32000; j += i) { initSieve[j] = true; } } if (i * i > 32000) break; } } public static void getAnswer() { for (int i = 2; i < 32000 && i <= right; i++) { if (!initSieve[i]) { int num = i; if (num * 2 >= left) { num *= 2; } else { num = (num * (left / num)); if (num < left) num += i; } for (int j = num; j >= left && j <= right; j += i) { answer[j - left] = true; } } } } }
阅读一些建议后,我已经编辑了解决方案。我仍然收到超过时间限制的错误。还有其他建议如何进一步优化吗?我计算直到32000的所有素数,然后使用这些素数找到n到m之间的素数。
谢谢罗希特
给你
1 <= m <= n <= 1000000000,nm <= 100000
这些是非常小的数字。要筛选上限为的范围n,您需要素数为√n。在这里,您知道n <= 10^9,因此√n < 31623,您最需要的质数为31621。存在3401。您可以在几微秒内用标准筛生成它们。
n
√n
n <= 10^9
√n < 31623
然后,您可以通过标记之前筛选过的质数的倍数来筛选从m到的较小范围n,当质数超过时停止√n。通过从筛子中消除一些小质数的倍数可以获得一定的提速,但是逻辑变得更加复杂(您需要对小筛子进行m特殊处理)。
m
public int[] chunk(int m, int n) { if (n < 2) return null; if (m < 2) m = 2; if (n < m) throw new IllegalArgumentException("Borked"); int root = (int)Math.sqrt((double)n); boolean[] sieve = new boolean[n-m+1]; // primes is the global array of primes to 31621 populated earlier // primeCount is the number of primes stored in primes, i.e. 3401 // We ignore even numbers, but keep them in the sieve to avoid index arithmetic. // It would be very simple to omit them, though. for(int i = 1, p = primes[1]; i < primeCount; ++i) { if ((p = primes[i]) > root) break; int mult; if (p*p < m) { mult = (m-1)/p+1; if (mult % 2 == 0) ++mult; mult = p*mult; } else { mult = p*p; } for(; mult <= n; mult += 2*p) { sieve[mult-m] = true; } } int count = m == 2 ? 1 : 0; for(int i = 1 - m%2; i < n-m; i += 2) { if (!sieve[i]) ++count; } int sievedPrimes[] = new int[count]; int pi = 0; if (m == 2) { sievedPrimes[0] = 2; pi = 1; } for(int i = 1 - m%2; i < n-m; i += 2) { if (!sieve[i]) { sievedPrimes[pi++] = m+i; } } return sievedPrimes; }
使用一个BitSet或任何其他类型的压缩标志数组将减少内存使用,因此由于具有更好的缓存位置,因此可以显着提高速度。
BitSet