这里是一段代码,使用该语句计算从2到1000的所有素数,数字n是素数iff:
在第一个版本中,我认为我正确地实现了算法:
public class Giuga {
public static void main(String[] args){
int n = 2;
while(n<=1000){
int k = 1;
long sum = 0;
while(k<=n-1){
sum = sum+(long)Math.pow((double)k,(double)n-1);
k++;
}
if(sum%n==n-1){
System.out.println(n + " is a prime.");
}
n++;
}
}
}
但是,由于变量sum
迅速增长,发生溢出,在素数17之后将不再有输出。
为了防止我必须使用它:
嗯,我做到了,这是我的2.版本:
public class Giuga {
public static void main(String[] args){
int n = 2;
while(n<=1000){
int k = 1;
long sum = 0;
while(k<=n-1){
sum = sum+((long)Math.pow((double)k%n,(double)n-1))%n; //Here are the changes
k++;
}
if(sum%n==n-1){
System.out.println(n + " is a prime.");
}
n++;
}
}
}
我认为我做得对,但现在输出在素数13之后停止。
我现在试图找出我的错误很长一段时间了。我究竟做错了什么? 从2到1000必须有168个素数。
答案 0 :(得分:7)
正如已经指出的那样,只有大约16位精度的double
s不够精确,无法在足够高的数字上保持正确的余数。
您可以切换到long
并执行自己的模幂运算。
int k = 1;
long sum = 0;
while(k<=n-1){
long pow = 1;
for (int i = 0; i < n - 1; i++)
pow = (pow * k) % n;
sum = (sum + pow)%n;
k++;
}
这种算法可以通过将这种简单的模幂运算改为通过重复平方使用模幂运算来改进,并且它不是最有效的素数查找算法,但它现在是正确的。
2 is a prime.
3 is a prime.
5 is a prime.
7 is a prime.
11 is a prime.
13 is a prime.
17 is a prime.
19 is a prime.
23 is a prime.
29 is a prime.
31 is a prime.
(剪断)
977 is a prime.
983 is a prime.
991 is a prime.
997 is a prime.
要通过重复平方进行模幂运算,请替换
long pow = 1;
for (int i = 0; i < n - 1; i++)
pow = (pow * k) % n;
与
long pow = 1;
long square = k;
int exp = n - 1;
while (exp > 0)
{
if ((exp & 1) == 1)
{
pow = (pow * square) % n;
}
square = (square * square) % n;
exp >>= 1;
}
它连续测试指数的每个位,并将当前的平方乘以pow
(如果已设置)。
答案 1 :(得分:3)
Java double
s(即pow
的输出)不能精确地表示大数以产生正确的余数。您应该切换到modular exponentiation。
答案 2 :(得分:2)
您始终可以使用BigInteger类进行大量计算
private static boolean isPrime(int n) {
BigInteger N = new BigInteger(String.valueOf(n));
BigInteger N_MINUS_1 = N.subtract(BigInteger.ONE);
BigInteger sum = BigInteger.ZERO;
for (int k = 1; k < n; k++)
sum = sum.add(new BigInteger(String.valueOf(k)).modPow(N_MINUS_1,N)).mod(N);
return sum.equals(N_MINUS_1);
}
有趣的是,这是Fermat's little theorem 的变体,对于总和Σ
中的每个kk ^(n-1)%n应为1,否则此数字不是素数!因此,如果我们发现k ^ k ^(n-1)%n!= 1,我们就可以停止计算。上述算法可以改写为:
private static boolean isPrimeFermat(int n) {
BigInteger N = new BigInteger(String.valueOf(n));
BigInteger N_MINUS_1 = N.subtract(BigInteger.ONE);
for (int k = 1; k < n; k++){
if (new BigInteger(String.valueOf(k)).modPow(N_MINUS_1, N).equals(BigInteger.ONE) == false)
return false;
}
return true;
}
瞧!
答案 3 :(得分:1)
你的力量也太大了(猜猜是999 ^ 999)。所以你必须使用逐步(ab) mod n = ((a mod n)(b mod n)) mod n
(模幂运算)来计算功率:
public class Giuga {
public static void main(String[] args) {
int count = 0;
for (int i = 2; i < 1000; i++) {
if (isPrime(i)) {
count++;
System.out.printf("%d is prime\n", i);
}
}
System.out.printf("Found %d primes.\n", count);
}
// checks if number is a prime
private static boolean isPrime(int number) {
int bigSum = 0;
for (int k = 1; k <= number - 1; k++) {
bigSum = (bigSum + calcPowMod(k, number - 1, number)) % number;
}
return bigSum % number == number - 1;
}
// calculates (a^b)%mod, making sure that intermediate results are always < max(a^2,mod)
private static int calcPowMod(int a, int b, int mod) {
int pow = 1;
for (int k = 1; k <= b; k++) {
pow = (pow * a) % mod;
}
return pow;
}
}