我在编程网站上遇到了以下问题: 彼得想为他的密码系统生成一些素数。帮助他!你的任务是生成两个给定数字之间的所有素数!
输入
输入以单行中的测试用例的数量t开始(t <= 10)。在下一个t行的每一行中,有两个数字m和n(1 <= m <= n <= 1000000000,n-m <= 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之间的素数。
谢谢, 罗希特夏尔
答案 0 :(得分:4)
你被给予
1&lt; = m&lt; = n&lt; = 1000000000,n-m <= 100000
这些是非常小的数字。要筛选上限为n
的范围,您需要√n
的素数。在这里你知道n <= 10^9
,所以√n < 31623
,所以你最需要的是31621的质数。有3401.你可以用几微秒的标准筛子生成它们。
然后,您可以通过标记之前已经过筛过的素数的倍数来简单地筛选从m
到n
的小范围,当素数超过√n
时停止。通过从筛子中消除一些小质数的倍数可以获得一些加速,但逻辑变得更复杂(你需要专门处理小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
或任何其他类型的压缩标志阵列可以减少内存使用量,因此可以通过更好的缓存局部性来显着提高速度。
答案 1 :(得分:1)
使用BitSet而不是布尔数组。
public static BitSet primes (final int MAX)
{
BitSet primes = new BitSet (MAX);
// make only odd numbers candidates...
for (int i = 3; i < MAX; i+=2)
{
primes.set(i);
}
// ... except no. 2
primes.set (2, true);
for (int i = 3; i < MAX; i+=2)
{
/*
If a number z is already eliminated (like 9),
because it is itself a multiple of a prime
(example: 3), then all multiples of z (9) are
already eliminated.
*/
if (primes.get (i))
{
int j = 3 * i;
while (j < MAX)
{
if (primes.get (j))
primes.set (j, false);
j += (2 * i);
}
}
}
return primes;
}
答案 2 :(得分:0)
你 HAVE 将结果存储在数组中吗?一个方法如何计算给定的整数是否为素数而只是为{left,left+1,...,right}
中的每个数字调用它?
答案 3 :(得分:0)
访问isNotPrime数组时,您始终可以使用偏移量。
给定m,n:
boolean[] isNotPrime = new boolean[n-m+1];
// to now if number x is primer or not
boolean xIsPrime = isNotPrime[x-m];
这里m是偏移量。
答案 4 :(得分:0)
您不必强制拥有一个大型数组:您可以保留到目前为止找到的素数列表,并使用多个数组进行测试,其值为= array_slot + offset(已测试的值)。完成从i到j的值后,将j-i添加到offset并从J开始创建一个新数组。
您可以从数组中删除偶数,这样可以节省一些空间(values = array_slot * 2 - 1)。
答案 5 :(得分:0)
由于m和n之间的距离相对较小,你可以在m和n之间的每个数字中强制使用快速素性测试算法。
如果您允许使用概率算法,则可以使用Miller-Rabin test。设M = n-m <= 10 ^ 5且N = n <= 10 ^ 9。蛮力算法的复杂性为O(k M(log N)^ 3),其中k是控制概率保证的常数(对于实际应用,k可以设置为10)。
对于问题的限制,这种复杂性将在10 ^ 9左右。