簡體   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