55

与えられた数が素数であるかどうかをチェックする最速の方法を見つけようとしています(Javaで)。以下は私が思いついたいくつかの素数判定方法です。2番目の実装(isPrime2)よりも良い方法はありますか?

public class Prime {
    public static boolean isPrime1(int n) {
        if (n <= 1) {
            return false;
        }
        if (n == 2) {
            return true;
        }
        for (int i = 2; i <= Math.sqrt(n) + 1; i++) {
            if (n % i == 0) {
                return false;
            }
        }
        return true;
    }
    public static boolean isPrime2(int n) {
        if (n <= 1) {
            return false;
        }
        if (n == 2) {
            return true;
        }
        if (n % 2 == 0) {
            return false;
        }
        for (int i = 3; i <= Math.sqrt(n) + 1; i = i + 2) {
            if (n % i == 0) {
                return false;
            }
        }
        return true;
    }
}

public class PrimeTest {
    public PrimeTest() {
    }
 
    @Test
    public void testIsPrime() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
 
        Prime prime = new Prime();
        TreeMap<Long, String> methodMap = new TreeMap<Long, String>();
 
        for (Method method : Prime.class.getDeclaredMethods()) {
 
            long startTime = System.currentTimeMillis();
 
            int primeCount = 0;
            for (int i = 0; i < 1000000; i++) {
                if ((Boolean) method.invoke(prime, i)) {
                    primeCount++;
                }
            }
 
            long endTime = System.currentTimeMillis();
 
            Assert.assertEquals(method.getName() + " failed ", 78498, primeCount);
            methodMap.put(endTime - startTime, method.getName());
        }
 
 
        for (Entry<Long, String> entry : methodMap.entrySet()) {
            System.out.println(entry.getValue() + " " + entry.getKey() + " Milli seconds ");
        }
    }
}
4

15 に答える 15

84

別の方法は次のとおりです。

boolean isPrime(long n) {
    if(n < 2) return false;
    if(n == 2 || n == 3) return true;
    if(n%2 == 0 || n%3 == 0) return false;
    long sqrtN = (long)Math.sqrt(n)+1;
    for(long i = 6L; i <= sqrtN; i += 6) {
        if(n%(i-1) == 0 || n%(i+1) == 0) return false;
    }
    return true;
}

32ビットすべてにBigInteger's isProbablePrime(...)有効ですint

編集

isProbablePrime(certainty)必ずしも正しい答えが得られるとは限らないことに注意してください。確実性が低い側にある場合、コメントで言及されているように、@dimo414が誤検知を生成します。

残念ながら、isProbablePrime(certainty)すべての(32ビット)に有効であると主張するソースを見つけることができませんでしたint(十分な確実性があれば!)。

そこで、いくつかのテストを実行しました。BitSetすべての不均一な数を表すサイズのを作成しInteger.MAX_VALUE/2、素数ふるいを使用して範囲内のすべての素数を見つけました1..Integer.MAX_VALUE。次に、からループしi=1..Integer.MAX_VALUEて、すべてをテストしましたnew BigInteger(String.valueOf(i)).isProbablePrime(certainty) == isPrime(i)

確実性5と10についてisProbablePrime(...)は、ラインに沿って誤検知が発生しました。しかし、isProbablePrime(15)では、テストは失敗しませんでした。

これが私のテストリグです:

import java.math.BigInteger;
import java.util.BitSet;

public class Main {

    static BitSet primes;

    static boolean isPrime(int p) {
        return p > 0 && (p == 2 || (p%2 != 0 && primes.get(p/2)));
    }

    static void generatePrimesUpTo(int n) {
        primes = new BitSet(n/2);

        for(int i = 0; i < primes.size(); i++) {
            primes.set(i, true);
        }

        primes.set(0, false);
        int stop = (int)Math.sqrt(n) + 1;
        int percentageDone = 0, previousPercentageDone = 0;
        System.out.println("generating primes...");
        long start = System.currentTimeMillis();

        for(int i = 0; i <= stop; i++) {
            previousPercentageDone = percentageDone;
            percentageDone = (int)((i + 1.0) / (stop / 100.0));

            if(percentageDone <= 100 && percentageDone != previousPercentageDone) {
                System.out.println(percentageDone + "%");
            }

            if(primes.get(i)) {
                int number = (i * 2) + 1;

                for(int p = number * 2; p < n; p += number) {
                    if(p < 0) break; // overflow
                    if(p%2 == 0) continue;
                    primes.set(p/2, false);
                }
            }
        }
        long elapsed = System.currentTimeMillis() - start;
        System.out.println("finished generating primes ~" + (elapsed/1000) + " seconds");
    }

    private static void test(final int certainty, final int n) {
        int percentageDone = 0, previousPercentageDone = 0;
        long start = System.currentTimeMillis();
        System.out.println("testing isProbablePrime(" + certainty + ") from 1 to " + n);
        for(int i = 1; i < n; i++) {
            previousPercentageDone = percentageDone;
            percentageDone = (int)((i + 1.0) / (n / 100.0));
            if(percentageDone <= 100 && percentageDone != previousPercentageDone) {
                System.out.println(percentageDone + "%");
            }
            BigInteger bigInt = new BigInteger(String.valueOf(i));
            boolean bigIntSays = bigInt.isProbablePrime(certainty);
            if(isPrime(i) != bigIntSays) {
                System.out.println("ERROR: isProbablePrime(" + certainty + ") returns "
                    + bigIntSays + " for i=" + i + " while it " + (isPrime(i) ? "is" : "isn't" ) +
                    " a prime");
                return;
            }
        }
        long elapsed = System.currentTimeMillis() - start;
        System.out.println("finished testing in ~" + ((elapsed/1000)/60) +
                " minutes, no false positive or false negative found for isProbablePrime(" + certainty + ")");
    }

    public static void main(String[] args) {
        int certainty = Integer.parseInt(args[0]);
        int n = Integer.MAX_VALUE;
        generatePrimesUpTo(n);
        test(certainty, n);
    }
}

私が実行したこと:

java -Xmx1024m -cp . Main 15

素数の生成には、私のマシンで約30秒かかりました。そして、オールiインの実際のテストに1..Integer.MAX_VALUEは約2時間15分かかりました。

于 2010-03-05T10:27:49.647 に答える
43

これが最もエレガントな方法です。

public static boolean isPrime(int n) {
    return !new String(new char[n]).matches(".?|(..+?)\\1+");
}

Java1.4以降。インポートは必要ありません。

とても短いです。とても美しい。

于 2010-12-23T03:42:29.333 に答える
12

2の倍数をすべて削除する最初のステップを実行しました。

しかし、なぜそこで止まったのですか?3を除く3の倍数をすべて削除し、5を除く5の倍数をすべて削除することもできます。

この推論に従って結論を出すと、エラトステネスのふるいが得られます。

于 2010-03-05T10:19:29.590 に答える
12

AKS素数性テスト(およびそのさまざまな最適化)を見てください。これは、多項式時間で実行される決定論的素数性テストです。

テュービンゲン大学(ドイツ)のJavaでのアルゴリズムの実装がここにあります

于 2010-03-05T10:24:01.417 に答える
5

Jaeschke(1993)による高速テストは、ミラーラビンテストの決定論的バージョンであり、4,759,123,141未満の誤検知がないため、Javaに適用できますint

// Given a positive number n, find the largest number m such
// that 2^m divides n.
private static int val2(int n) {
  int m = 0;
  if ((n&0xffff) == 0) {
    n >>= 16;
    m += 16;
  }
  if ((n&0xff) == 0) {
    n >>= 8;
    m += 8;
  }
  if ((n&0xf) == 0) {
    n >>= 4;
    m += 4;
  }
  if ((n&0x3) == 0) {
    n >>= 2;
    m += 2;
  }
  if (n > 1) {
    m++;
  }
  return m;
}

// For convenience, handle modular exponentiation via BigInteger.
private static int modPow(int base, int exponent, int m) {
  BigInteger bigB = BigInteger.valueOf(base);
  BigInteger bigE = BigInteger.valueOf(exponent);
  BigInteger bigM = BigInteger.valueOf(m);
  BigInteger bigR = bigB.modPow(bigE, bigM);
  return bigR.intValue();
}

// Basic implementation.
private static boolean isStrongProbablePrime(int n, int base) {
  int s = val2(n-1);
  int d = modPow(base, n>>s, n);
  if (d == 1) {
    return true;
  }
  for (int i = 1; i < s; i++) {
    if (d+1 == n) {
      return true;
    }
    d = d*d % n;
  }
  return d+1 == n;
}

public static boolean isPrime(int n) {
  if ((n&1) == 0) {
    return n == 2;
  }
  if (n < 9) {
    return n > 1;
  }

  return isStrongProbablePrime(n, 2) && isStrongProbablePrime(n, 7) && isStrongProbablePrime(n, 61);
}

これは変数では機能しませんlongが、別のテストでは機能します。BPSWテストには2^64までの反例がありません。これは基本的に、上記のような2つの確率的素数テストと、それに続くもう少し複雑ですが根本的に異なるわけではない強力なルーカステストで構成されます。

これらのテストは両方とも、どの種類の試行割り算よりもはるかに高速です。

于 2017-01-30T22:21:38.120 に答える
4

あなたのアルゴリズムは、適度に少ない数でうまく機能します。大きな数の場合は、高度なアルゴリズムを使用する必要があります(たとえば、楕円曲線に基づく)。もう1つのアイデアは、いくつかの「疑似素数」テストを使用することです。これらは、数値が素数であることをすばやくテストしますが、100%正確ではありません。ただし、アルゴリズムよりも迅速にいくつかの数値を除外するのに役立ちます。

最後に、コンパイラはおそらくこれを最適化しますが、次のように書く必要があります。

int max =  (int) (Math.sqrt(n) + 1);
for (int i = 3; i <= max; i = i + 2) {
}
于 2010-03-05T10:21:58.240 に答える
4

数が素数であるかどうかを調べようとしているだけなら十分ですが、0からnaまでのすべての素数を見つけようとしている場合は、エラトステネスのふるいがより良い選択肢になります。

ただし、配列サイズなどのJavaの制限に依存します。

于 2010-03-05T10:23:03.183 に答える
4

この方法が最適だと思います。少なくとも私にとっては-

    public static boolean isPrime(int num)
    {
        for (int i = 2; i<= num/i; i++)
        {
            if (num % i == 0)
            {
                return false;
            }
        }
        return num > 1;
    }
于 2014-11-02T14:09:40.047 に答える
4

もちろん、何百もの素数性テストがあり、すべて、数のサイズ、特殊な形式、因子のサイズなどに基づいて、さまざまな長所と短所があります。

ただし、Javaでは、これが最も便利なものだと思います。

BigInteger.valueOf(long/int num).isProbablePrime(int certainty);

すでに実装されており、非常に高速で(1000x1000の行列が0〜2 ^ 64の長さと15の確実性で満たされている場合は約6秒かかります)、おそらく人間が思いつくものよりも最適化されています。

これは、既知の反例がないバージョンのBaillie-PSW素数性テストを使用します。(ただし、テストの少し弱いバージョンを使用する場合がありますが、エラーが発生する場合があります。多分)

于 2017-03-23T23:46:32.060 に答える
3

あなたが書いたことは、最も一般的なプログラマーが行うことであり、ほとんどの場合それで十分なはずです。

ただし、「最高の科学的アルゴリズム」を求めている場合は、 http://en.wikipedia.org/wiki/Prime_numberに記載されている多くのバリエーション(確実性のレベルはさまざまです)があります。

たとえば、70桁の数字がある場合、JVMの物理的な制限によりコードの実行が妨げられる可能性があります。その場合は、「ふるい」などを使用できます。

繰り返しますが、これがプログラミングの質問であるか、ソフトウェアでの使用法の一般的な質問である場合、あなたのコードは完璧でなければなりません:)

于 2010-03-05T10:20:22.617 に答える
3

テストする必要のある数の長さに応じて、小さい値(n <10 ^ 6)の素数のリストを事前に計算できます。これは、求められた数がこの範囲内にある場合に最初に使用されます。もちろん、これが最速の方法です。他の回答で述べられているように、エラトステネスのふるいは、そのような事前計算されたリストを生成するための好ましい方法です。

あなたの数がこれよりも大きい場合は、Rabinの素数性テストを使用できます。 ラビン素数性テスト

于 2010-03-05T10:23:11.513 に答える
1

アルゴリズム効率:O(n ^(1/2))アルゴリズム

注:以下のサンプルコードには、結果を出力するためのカウント変数とprint関数の呼び出しが含まれています。

import java.util.*;

class Primality{
    private static void printStats(int count, int n, boolean isPrime) {

        System.err.println( "Performed " + count + " checks, determined " + n
        + ( (isPrime) ? " is PRIME." : " is NOT PRIME." ) );
    }
    /**
    *   Improved O( n^(1/2)) ) Algorithm
    *    Checks if n is divisible by 2 or any odd number from 3 to sqrt(n).
    *    The only way to improve on this is to check if n is divisible by 
    *   all KNOWN PRIMES from 2 to sqrt(n).
    *
    *   @param n An integer to be checked for primality.
    *   @return true if n is prime, false if n is not prime.
    **/
    public static boolean primeBest(int n){
        int count = 0;
        // check lower boundaries on primality
        if( n == 2 ){ 
            printStats(++count, n, true);
            return true;
        } // 1 is not prime, even numbers > 2 are not prime
        else if( n == 1 || (n & 1) == 0){
            printStats(++count, n, false);
            return false;
        }

        double sqrtN = Math.sqrt(n);
        // Check for primality using odd numbers from 3 to sqrt(n)
        for(int i = 3; i <= sqrtN; i += 2){
            count++;
            // n is not prime if it is evenly divisible by some 'i' in this range
            if( n % i == 0 ){ 
                printStats(++count, n, false);
                return false;
            }
        }
        // n is prime
        printStats(++count, n, true);
        return true;
    }

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        while(scan.hasNext()) {
            int n = scan.nextInt();
            primeBest(n);
            System.out.println();
        }
        scan.close();
    }
}

素数2147483647を入力すると、次の出力が生成されます。

23170チェックを実行し、2147483647がPRIMEであると判断しました。

于 2017-01-30T16:15:21.757 に答える
0

Intel Atom @ 1.60GHz、2GB RAM、32ビットオペレーティングシステムでテスト済み

テスト結果:
Long.MAX_VALUE=9223372036854775807より下の最大素数は9223372036854775783
経過時間は171499ミリ秒または2分51秒です

public class PrimalityTest
{
    public static void main(String[] args)
    {
        long current_local_time = System.currentTimeMillis();
        long long_number = 9223372036854775783L;
        long long_a;
        long long_b;
        if (long_number < 2)
        {
            System.out.println(long_number + " is not a prime number");
        }
        else if (long_number < 4)
        {
            System.out.println(long_number + " is a prime number");
        }
        else if (long_number % 2 == 0)
        {
            System.out.println(long_number + " is not a prime number and is divisible by 2");
        }
        else
        {
            long_a = (long) (Math.ceil(Math.sqrt(long_number)));
            terminate_loop:
            {
                for (long_b = 3; long_b <= long_a; long_b += 2)
                {
                    if (long_number % long_b == 0)
                    {
                        System.out.println(long_number + " is not a prime number and is divisible by " + long_b);
                        break terminate_loop;
                    }
                }
                System.out.println(long_number + " is a prime number");
            }
        }
        System.out.println("elapsed time: " + (System.currentTimeMillis() - current_local_time) + " millisecond/s");
    }
}
于 2018-09-27T02:47:47.120 に答える
0

何よりもまず、素数は2から始まります。2と3は素数です。素数は2または3で割り切れないようにする必要があります。残りの素数は6k-1および6k+1の形式です。SQRT(入力)までの数値を確認する必要があることに注意してください。このアプローチは非常に効率的です。お役に立てば幸いです。

public class Prime {

    public static void main(String[] args) {
        System.out.format("%d is prime: %s.\n", 199, isPrime(199)); // Prime
        System.out.format("%d is prime: %s.\n", 198, isPrime(198)); // Not prime
        System.out.format("%d is prime: %s.\n", 104729, isPrime(104729)); // Prime
        System.out.format("%d is prime: %s.\n", 104727, isPrime(982443529)); // Prime
    }

    /**
     * Tells if a number is prime or not.
     *
     * @param input the input
     * @return If the input is prime or not
     */
    private boolean isPrime(long input) {
    if (input <= 1) return false; // Primes start from 2
    if (input <= 3) return true; // 2 and 3 are primes
    if (input % 2 == 0 || input % 3 == 0) return false; // Not prime if dividable by 2 or 3
    // The rest of the primes are in the shape of 6k-1 and 6k+1
    for (long i = 5; i <= Math.sqrt(input); i += 6) if (input % i == 0 || input % (i + 2) == 0) return false;
    return true;
    }

}
于 2020-04-21T17:24:23.500 に答える
0

一般に、いくつかの素数階乗整数より大きいすべての素数Cは、次の形式Ck+ii < Cありikは整数でありi、互いに素である数を表します。C

これはの例ですC=30。BartKiersが答えるよりも速く動作するはずでC=6あり、計算することで改善できます。C=210

boolean isPrime(long n) {
    if(n < 2){
        return false;
    }
    if(n == 2 || n == 3 || n == 5 || n == 7 || n == 11 || n == 13 || n == 17 || n == 19 || n == 23 || n == 29){
        return true;
    }
    
    long sqrtN = (long) Math.sqrt(n) + 1;
    int[] mods = {1, 7, 11, 13, 17, 19, 23, 29};
    for (long i = 30L; i <= sqrtN; i += 30) {
        for (int mod : mods) {
            if(n % (i + mod) == 0){
                return false;
            }
        }
    }
    return true;
}
于 2020-08-07T17:25:58.540 に答える