简体   繁体   English

查找从2到1000的所有素数不起作用的算法

[英]Algorithm to find all primes from 2 to 1000 not working

Here's a piece of code to compute all primes from 2 to 1000 using the statement, that a number n is a prime number iff: 这是使用该语句计算从2到1000的所有素数的一段代码,数字n是素数iff:

在此输入图像描述

In the first version I think that I implemented the algorithm correctly: 在第一个版本中,我认为我正确地实现了算法:

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++;
        }
    }
}

But, since the variable sum grows rapidly, an overflow happens and after the prime number 17 there will be no output anymore. 但是,由于变量sum快速增长,发生溢出,在素数17之后将不再有输出。

To prevent that I have to use this: 为了防止我必须使用这个:

在此输入图像描述

Well, I did that and here is my 2. version: 好吧,我做到了,这是我的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++;
        }
    }
}

I think I did it correctly, but now the output stops after the prime number 13. 我想我做得对,但现在输出在素数13之后停止。

I'm trying to find my mistake for quite some time now. 我现在试图找出我的错误很长一段时间了。 What am I doing wrong? 我究竟做错了什么? There must be 168 primes from 2 to 1000. 从2到1000必须有168个素数。

As has been pointed out, double s, which only have about 16 digits of precision, aren't precise enough to maintain the correct remainders for calculations on high enough numbers. 正如已经指出的那样,只有大约16位精度的double s不够精确,无法保持正确的剩余部分,以便在足够高的数字上进行计算。

You can switch to long s and perform you own modular exponentiation. 您可以切换到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++;
}

This algorithm could be improved by changing this straightforward modular exponentiation to using modular exponentiation by repeated squaring, and it's not the most efficient prime finding algorithm, but it is now correct. 这种算法可以通过将这种简单的模幂运算改为通过重复平方使用模幂运算来改进,并且它不是最有效的寻找算法,但它现在是正确的。

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.

(snip) (剪断)

977 is a prime.
983 is a prime.
991 is a prime.
997 is a prime.

To make it modular exponentiation by repeated squaring, replace 通过重复平方使其进行模幂运算,替换

long pow = 1;
for (int i = 0; i < n - 1; i++)
    pow = (pow * k) % n;

with

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;
}

It tests each bit of the exponent in succession, and multiplies the current square in to pow if it's set. 它连续测试指数的每个位,并将当前的平方乘以pow如果已设置)。

Java double s (ie, the outputs of pow ) do not represent large numbers precisely enough to yield a correct remainder. Java double s(即pow的输出)并不足以精确地表示大数以产生正确的余数。 You should switch to modular exponentiation . 您应该切换到模幂运算

You can always use the BigInteger class for large number computations 您始终可以使用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);
}

It's interesting though that this is a variation of Fermat's little theorem , for each k in the sum Σ 有趣的是, 这是费马小定理的变量 ,对于和Σ中的每个k

k^(n-1)%n should be 1, else this number is not prime! k ^(n-1)%n应该是1,否则这个数字不是素数! So, if we find ak that k^(n-1)%n != 1, we can stop calculations. 所以,如果我们发现k ^(n-1)%n!= 1,我们可以停止计算。 The above algorithm can be rewritten as: 上述算法可以改写为:

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;
}

Voilà! 瞧!

Your powers are too big as well (guess what 999^999 is). 你的力量也太大了(猜猜999 ^ 999是多少)。 So you have to calculate the power as well using stepwise (ab) mod n = ((a mod n)(b mod n)) mod n ( modular exponentiation ): 所以你必须使用逐步(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;
    }
}

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

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