素数を見つける最速のアルゴリズムはどれですか?


183

C ++を使用して素数を見つける最速のアルゴリズムはどれですか?ふるいのアルゴリズムを使用しましたが、それでも高速にしたいです!


私が見つけた古い記事ですが、面白
Mvcoile

29
@Jaiderは、7(111)の数字では失敗します。1001 = 9の場合も失敗します。そして、明らかにそれは一般的にほとんどすべての素数で失敗します(ケース2 ^ p-1はカバーしません。これはメルセンヌの素数-古典的に生成された例-常に111 ... 1の形式になります)
ダニエルキャッツ

1
@Kasperasky-あなたはどのふるいに言及しませんでしたか?おそらく、Eranthosesのふるいを意味します!
user2618142 2017年

エラトステネスアルゴリズムのふるい
Emad Aghayi

回答:


79

アトキンふるいの非常に高速な実装は、Dan Bernsteinの素数です。このふるいはより効率的であるエラトステネスのふるい。彼のページにはいくつかのベンチマーク情報があります。


10
実際、primegenが最速だとは思いません。yafuとprimesieveはどちらも一般的に高速で、確かに2 ^ 32を超えています。どちらも、Atkin-Bernsteinふるいではなく、エラトステネスの(変更された)ふるいです。
チャールズ

5
Primesieve Sieve of Eratosthenes(SoE)は、可能な限り最も高速なアルゴリズムであり、この回答にリンクされているBernsteinを含め、Sieve of Atkin SoAの実装よりも常に高速です。ビット数の範囲(2 ^ 32-1)、primesieveは約12億のカルを実行しますが、SoAは合計で約14億のトグル操作とスクエアフリー操作を組み合わせて実行します。どちらの操作もほぼ同じ複雑さで、ほぼ同じように最適化できます。仕方。
GordonBGood 2013

7
続き:Bernsteinは、SoAと同じ効果的なホイール分解を使用してSoEのみを比較しました。これは2; 3; 5ホイールであり、このホイールを使用すると、32ビットの数値範囲で約18.3億のカルが発生します。これにより、SoEのこの制限されたバージョンを同等の他の最適化と比較する場合に、SoAが約30%速くなります。ただし、primesieeveアルゴリズムでは、2; 3; 5; 7ホイールと2; 3; 5; 7; 11; 13; 17ホイールセグメントプレカリングを組み合わせて使用​​し、操作の数を約12億に減らして実行します。同等の操作ループ最適化により、SoAより16.7%高速。
GordonBGood 2013

6
続き2:2; 3; 5因数分解ホイールはアルゴリズムの「組み込まれた」部分であるため、SoA conには、違いを大きくするために使用されるより高い因数ホイール因数分解がありません。
GordonBGood 2013

4
@Eamon Nerbonne、WPは正しいです。ただし、計算の複雑さがわずかに改善されただけでは、一般的なアルゴリズムの高速化にはなりません。これらのコメントでは、エラトステネスのふるい(SoE)の最大ホイール因数分解(Atkin-SoAのふるいでは不可能)によって、SoEの演算がわずかに少なくなり、最大で約10億の範囲になることがわかります。その点をはるかに超えると、一般にメモリ制限を克服するためにページ分割を使用する必要があり、SoAが失敗し、範囲の増加に伴って急速に増加する一定のオーバーヘッドがかかります。
GordonBGood 2014年

29

本当に高速でなければならない場合は、素数のリストを含めることができます:http :
//www.bigprimes.net/archive/prime/

特定の数が素数であるかどうかを知る必要がある場合は、ウィキペディアにさまざまな素数テストがリストされています。特に、数値が素数でないかどうかを知ることができるため、大きな数が素数かどうかを判断するためのおそらく最も速い方法です。


2
すべての素数のリスト?私はあなたが最初のいくつかの素数のリストを意味すると思います... :)
j_random_hacker

9
100000000を数回呼び出すと、はい。:)
GeorgSchölly2009年

58
確かに100000000は無限大と比較して「少数」です;)
Timofey

9
なぜアトキンのふるい(SoA)がエラトステネスのふるい(SoE)より速いと思いますか?あなたがリンクしたWikipediaの記事のように、疑似コードを使用してプログラムを実装するだけの場合ではありません。SoEがSoAで使用されるのと同じレベルの可能な最適化で実装されている場合、SoAの場合と比べて非常に大きなふるい範囲の場合の操作はわずかに少なくなりますが、そのゲインは通常、複雑度の増加とこの計算の複雑さによる余分な定数係数のオーバーヘッドにより、実用的なアプリケーションではSoEの方が優れています。
GordonBGood 2014年

26

彼、彼は私が古い質問に答えるネクロマンサーの質問であることを知っていますが、効率的な素数検定を実装する方法をネットで検索しているこの質問を見つけました。

今までのところ、最速の素数検定アルゴリズムは、Strong Probable Prime(SPRP)だと思います。私はNvidia CUDAフォーラムから引用しています:

数論におけるより実用的なニッチな問題の1つは、素数の識別に関係しています。Nが与えられた場合、それが素数かどうかを効率的に判断するにはどうすればよいですか?これは単なる理論上の問題ではなく、コードで必要な実際の問題である可能性があります。おそらく、特定の範囲内で主要なハッシュテーブルサイズを動的に見つける必要がある場合です。Nが2 ^ 30のオーダーの場合、30000の除算テストを実行して因子を検索しますか?明らかにそうではありません。

この問題に対する一般的な実用的な解決策は、オイラー確率素数テストと呼ばれる単純なテストと、強力確率素数(SPRP)と呼ばれるより強力な一般化です。これは、整数Nが確率的に素数であるかどうかを分類できるテストであり、テストを繰り返すと正しさの確率が高まります。テスト自体の遅い部分は主に、Nを法とするA ^(N-1)に類似した値の計算に関係します。RSA公開鍵暗号化バリアントを実装する人は誰でもこのアルゴリズムを使用しています。これは、巨大な整数(512ビットなど)と通常の32ビットまたは64ビット整数の両方に役立ちます。

Nの範囲で常に成功することが知られている特定のテスト入力パラメーターを事前計算することにより、テストを確率的棄却から素数性の決定的な証明に変更できます。残念ながら、これらの「最もよく知られているテスト」の発見は、膨大な(実際には無限)ドメイン。1980年に、有用なテストの最初のリストがCarl Pomeranceによって作成されました(二次SeiveアルゴリズムでRSA-129を因数分解することで有名です)。後にJaeschkeが1993年に結果を大幅に改善しました。2004年、ZhangとTangは理論を改善しました検索ドメインの制限。GreathouseとLivingstoneはこれまで、最新の検索結果をウェブで公開しており、巨大な検索ドメインの最高の検索結果であるhttp://math.crg4.com/primes.htmlで公開しています。

詳細については、http//primes.utm.edu/prove/prove2_3.htmlおよびhttp://forums.nvidia.com/index.php?showtopic=70483を参照してください。

非常に大きな素数を生成する方法が必要で、すべての素数<整数nを生成する必要がない場合は、Lucas-Lehmerテストを使用してメルセンヌの素数を検証できます。メルセンヌ素数は2 ^ p -1の形式です。ルーカス・レーマー検定はメルセンヌの素数で発見された最速のアルゴリズムだと思います。

また、最速のアルゴリズムだけでなく最速のハードウェアも使用したい場合は、Nvidia CUDAを使用して実装し、CUDA用のカーネルを作成してGPUで実行してください。

十分な素数を見つければ、いくらかお金を稼ぐこともできます。EFFは$ 50Kから$ 250Kの賞金を提供していますhttps : //www.eff.org/awards/coop


17

数値Pが素数であるか複合であるかをチェックする100%数学テストがあり、これはAKS素数テストと呼ばれます。

概念は単純です。数値が与えられ、Pすべての係数(x-1)^P - (x^P-1)がで割り切れる場合P、それPは素数であり、それ以外の場合、それは合成数です。

たとえば、が与えられた場合、P = 3多項式が得られます。

   (x-1)^3 - (x^3 - 1)
 = x^3 + 3x^2 - 3x - 1 - (x^3 - 1)
 = 3x^2 - 3x

また、係数はどちらもで割り切れる3ので、数値は素数です。

そしてP = 4、素数ではないの例では、

   (x-1)^4 - (x^4-1)
 = x^4 - 4x^3 + 6x^2 - 4x + 1 - (x^4 - 1)
 = -4x^3 + 6x^2 - 4x

そして、ここでは、係数6がで割り切れない4ため、素数ではないことがわかります。

多項式(x-1)^PP+1項になり、組み合わせを使用して見つけることができます。したがって、このテストはランタイムでO(n)実行されるためi、0からまで反復しpて残りをテストできるため、これがどれほど役立つかわかりません。


5
AKSは実際には非常に遅い方法であり、他の既知の方法と競合しません。あなたが説明する方法は、AKSではなく、(指摘したように)最適化されていない試行分割よりも遅い開始補題です。
DanaJ 2014年

こんにちは@Kousha、何のx略ですか?の中で(x-1)^P - (x^P-1)。これのサンプルコードはありますか?整数が素数であるかどうかを決定するためのC ++で?
kiLLua 2016年

@kiLLua Xは単なる変数です。数値が素数かどうかを決定するのはXの係数です。そして、私はコードを持っていません。数値が素数かどうかを判断するためにこの方法を実際に使用することはお勧めしません。これは素数の非常にクールな数学的動作ですが、それ以外の場合は非常に非効率的です。
Kousha 2016年

5

あなたの問題は、特定の数が素数であるかどうかを決定することですか?次に、素数テストが必要です(簡単)。または、指定された数までのすべての素数が必要ですか?その場合、素数のふるいは適切です(簡単ですが、メモリが必要です)。それとも、数の素因数が必要ですか?これには因数分解が必要になります(最も効率的な方法が本当に必要な場合、多数の場合は困難です)。あなたが見ている数はどれくらい大きいですか?16ビット?32ビット?より大きい?

賢明で効率的な方法の1つは、素数のテーブルを事前に計算し、ビットレベルのエンコーディングを使用してファイルに保持することです。ファイルは1つの長いビットベクトルと見なされますが、ビットnは整数nを表します。nが素数の場合、そのビットは1に設定され、それ以外の場合はゼロに設定されます。ルックアップは非常に高速で(バイトオフセットとビットマスクを計算します)、ファイルをメモリにロードする必要はありません。


優れた素数性テストは、適度に適合できる素数テーブルのメインメモリレイテンシと競合するため、L2に適合しない限り、これを使用しません。
Charles、

3

Rabin-Millerは、標準的な確率素数テストです。(K回実行すると、入力数は確実に合成されるか、おそらくエラー4 -Kの確率で素数になります。(数百回の反復で、ほぼ確実に真実を伝えています)

Rabin Millerの非確率論的(決定論的)バリアントがあります。

グレートインターネットメルセンヌ数検索最大の実績のあるプライムのための世界記録発見した(GIMPS)(2 74207281 - 2017年6月の時点で1)は、使用していますいくつかのアルゴリズムを、これらは特別な形で素数です。ただし、上記のGIMPSページには、一般的な決定論的素数性テストが含まれています。それらは、どのアルゴリズムが「最速」かは、テストする数のサイズに依存することを示しているようです。数値が64ビットに収まる場合は、数百万桁の素数を処理することを目的とした方法を使用しないでください。


2

アプリケーションによって異なります。考慮事項がいくつかあります。

  • いくつかの数が素数であるか、特定の制限までのすべての素数が必要か、または(潜在的に)すべての素数が必要かどうかの情報だけが必要ですか?
  • 対処しなければならない数字はどれくらいですか?

Miller-Rabinとアナログのテストは、特定のサイズ(数百万程度)を超える数のふるいよりも高速です。その下では、試験除算(数が少ない場合)またはふるいを使用する方が高速です。


-1

私は常にこのメソッドを使用して、ふるいアルゴリズムに従って素数を計算します。

void primelist()
 {
   for(int i = 4; i < pr; i += 2) mark[ i ] = false;
   for(int i = 3; i < pr; i += 2) mark[ i ] = true; mark[ 2 ] = true;
   for(int i = 3, sq = sqrt( pr ); i < sq; i += 2)
       if(mark[ i ])
          for(int j = i << 1; j < pr; j += i) mark[ j ] = false;
  prime[ 0 ] = 2; ind = 1;
  for(int i = 3; i < pr; i += 2)
    if(mark[ i ]) ind++; printf("%d\n", ind);
 }

-1

最速かどうかを判断させていただきます。

using System;
namespace PrimeNumbers
{

public static class Program
{
    static int primesCount = 0;


    public static void Main()
    {
        DateTime startingTime = DateTime.Now;

        RangePrime(1,1000000);   

        DateTime endingTime = DateTime.Now;

        TimeSpan span = endingTime - startingTime;

        Console.WriteLine("span = {0}", span.TotalSeconds);

    }


    public static void RangePrime(int start, int end)
    {
        for (int i = start; i != end+1; i++)
        {
            bool isPrime = IsPrime(i);
            if(isPrime)
            {
                primesCount++;
                Console.WriteLine("number = {0}", i);
            }
        }
        Console.WriteLine("primes count = {0}",primesCount);
    }



    public static bool IsPrime(int ToCheck)
    {

        if (ToCheck == 2) return true;
        if (ToCheck < 2) return false;


        if (IsOdd(ToCheck))
        {
            for (int i = 3; i <= (ToCheck / 3); i += 2)
            {
                if (ToCheck % i == 0) return false;
            }
            return true;
        }
        else return false; // even numbers(excluding 2) are composite
    }

    public static bool IsOdd(int ToCheck)
    {
        return ((ToCheck % 2 != 0) ? true : false);
    }
}
}

2.40 GHzプロセッサを搭載したCore 2 Duoラップトップで、1〜1,000,000の範囲内の素数を見つけて出力するには、約82秒かかります。そして、78,498の素数を見つけました。


3
これは非常に遅い方法です。問題ですi <= (ToCheck / 3)。それはあるはずですi <= (ToCheck / i)。それを使用すると、代わりに0.1秒で実行される可能性があります。
ネスは

-3
#include<stdio.h>
main()
{
    long long unsigned x,y,b,z,e,r,c;
    scanf("%llu",&x);
    if(x<2)return 0;
    scanf("%llu",&y);
    if(y<x)return 0;
    if(x==2)printf("|2");
    if(x%2==0)x+=1;
    if(y%2==0)y-=1;
    for(b=x;b<=y;b+=2)
    {
        z=b;e=0;
        for(c=2;c*c<=z;c++)
        {
            if(z%c==0)e++;
            if(e>0)z=3;
        }
        if(e==0)
        {
            printf("|%llu",z);
            r+=1;
        }
    }
    printf("|\n%llu outputs...\n",r);
    scanf("%llu",&r);
}    

rは初期化前に使用されます
zumalifeguard

-3

定義済みのアルゴリズムについては知りませんが、非常に高速な独自のアルゴリズムを作成しました。20桁の数値を1秒未満で処理できます。このプログラムの最大機能は18446744073709551615です。プログラムは次のとおりです。

#include <iostream>
#include <cmath>
#include <stdlib.h>

using namespace std;

unsigned long long int num = 0;

bool prime() {
    if (num % 2 == 0 || num == 1) {
        return false;
    }

    unsigned long int square_root = sqrt(num);
    for (unsigned long int i = 3; i <= square_root; i += 2) {
        if (num % i == 0) {
            return false;
        }
    }

    return true;
}

int main() {
    do {
        system("cls");
        cout << "Enter number : ";
        cin >> num;

        if (prime()) {
            cout << "The number is a prime number" << endl << endl << endl << endl;
        } else {
            cout << "The number is not a prime number" << endl << endl << endl << endl;
        }
        system("pause");
    } while (1);

    return 0;
}

-4
#include <iostream>

using namespace std;

int set [1000000];

int main (){

    for (int i=0; i<1000000; i++){
        set [i] = 0;
    }
    int set_size= 1000;
    set [set_size];
    set [0] = 2;
    set [1] = 3;
    int Ps = 0;
    int last = 2;

    cout << 2 << " " << 3 << " ";

    for (int n=1; n<10000; n++){
        int t = 0;
        Ps = (n%2)+1+(3*n);
        for (int i=0; i==i; i++){
            if (set [i] == 0) break;
            if (Ps%set[i]==0){
                t=1;
                break;
            }
        }
        if (t==0){
            cout << Ps << " ";
            set [last] = Ps;
            last++;
        }
    }
    //cout << last << endl;


    cout << endl;

    system ("pause");
    return 0;
}

12
これは、「GOTOを実際に使用せずに非構造化コードを作成する方法」に対する答えになるはずです。単純なトライアルディビジョンをコーディングするためのこの混乱すべて!(n%2)+1+(3*n)ちょっといいけ​​ど。:)
Will Ness

1
@Will Ness私はこれをその質問への回答として反対票を投じたでしょう。マクロが実行するときにforループを使用する理由 :)
Rob Grant

-4

私はそれが少し後であることを知っていますが、これは検索からここに到着した人々に役立つかもしれません。とにかく、ここでは、テストする必要があるのは素因数のみであるという事実に依存するJavaScriptです。そのため、コードによって生成された初期素数は、後の素数のテスト因数として再利用されます。もちろん、すべての偶数とmod 5の値が最初にフィルターで除外されます。結果は配列Pにあり、このコードはi7 PCで1.5秒未満で1000万個の素数を処理できます(約20で1億個)。Cで書き直された場合、非常に高速になるはずです。

var P = [1, 2], j, k, l = 3

for (k = 3 ; k < 10000000 ; k += 2)
{
  loop: if (++l < 5)
  {
    for (j = 2 ; P[j] <= Math.sqrt(k) ; ++j)
      if (k % P[j] == 0) break loop

    P[P.length] = k
  }
  else l = 0
}

2
これは、多数の素数を生成する場合に多くの問題を引き起こします。比較では、sqrtがかなり遅いため、P [j] * P [j] <= kを使用することをお勧めします
Simon

-11
#include<iostream>
using namespace std;

void main()
{
    int num,i,j,prime;
    cout<<"Enter the upper limit :";
    cin>>num;

    cout<<"Prime numbers till "<<num<<" are :2, ";

    for(i=3;i<=num;i++)
    {
        prime=1;
        for(j=2;j<i;j++)
        {
            if(i%j==0)
            {
                prime=0;
                break;
            }
        }

        if(prime==1)
            cout<<i<<", ";

    }
}

60
これはあなたがそれについて行くことができる最も遅い程度です。
ネスは

1
これは非常に遅く、上限が10000000だとすると、このコードは多くの時間を消費します!!
Dixit Singla 2013年

このコードはO(N ^ 2 / log N)です。break;それなしではO(N ^ 2)はさらに遅くなりますが、それはすでにコーディングエラーと見なすことができます。素数による保存とテストはO(N ^ 2 /(log N)^ 2)であり、数値の平方根以下の素数によるテストはO(N ^ 1.5 /(log N)^ 2)です。
ネス

@WillNessおそらく少し双曲線的でしょう。彼はforループを2ではなく1から簡単に開始でき、j <iの代わりにj <= iを追加できました。:)
ケニーケイソン

3
この投稿は削除すべきではないと思います。貴重な反例として機能します。
ネス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.