繁体   English   中英

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

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

这是使用该语句计算从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个素数。

正如已经指出的那样,只有大约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如果已设置)。

Java double s(即pow的输出)并不足以精确地表示大数以产生正确的余数。 您应该切换到模幂运算

您始终可以使用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);
}

有趣的是, 这是费马小定理的变量 ,对于和Σ中的每个k

k ^(n-1)%n应该是1,否则这个数字不是素数! 所以,如果我们发现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;
}

瞧!

你的力量也太大了(猜猜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;
    }
}

暂无
暂无

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

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