ミラー・ラビンの強い擬プライム


16

非負の整数Nを指定すると、すべての最初のN素数の基底に対する強い擬似素数である最小の奇数の正の整数を出力します。

これは、OEISシーケンスA014233です。

テストケース(1インデックス付き)

1       2047
2       1373653
3       25326001
4       3215031751
5       2152302898747
6       3474749660383
7       341550071728321
8       341550071728321
9       3825123056546413051
10      3825123056546413051
11      3825123056546413051
12      318665857834031151167461
13      3317044064679887385961981

のテストケースはN > 13、これらの値がまだ見つかっていないため利用できません。シーケンス内の次の用語を見つけることができた場合は、必ずOEISに提出してください!

ルール

  • Nインデックスがゼロまたはインデックスが1の値を選択できます。
  • ソリューションが言語の整数範囲内で表現可能な値(つまりN = 12、符号なし64ビット整数まで)でのみ動作することは許容されますが、ソリューションは、言語が任意の長さの整数をサポートするという前提で、理論的には入力に対して動作する必要があります。

バックグラウンド

正の整数は、奇数のx形式x = d*2^sで記述できますddそして、商が2で割り切れなくなるまで2でs繰り返し除算nすることで計算できます。これdはその最終商でありs、2が除算される回数ですn

正の整数nが素数の場合、フェルマーの小定理は次のように述べます。

フェルマー

任意の有限体 Z/pZ(どこにp素数がある)でも、の平方根11and -1(または同等に1and p-1)のみです。

これら3つの事実を使用して、次の2つのステートメントのいずれかが素数に対して真でなければならないことを証明できますnd*2^s = n-1およびrはの整数です[0, s))。

ミラーラビン条件

ミラー-ラビン素数判定法は、上記請求項のcontrapositiveを試験することによって動作する。ベースがあればa、上記の両方の条件が偽であるように、次いで、n素数ではありません。そのベースa証人と呼ばれます。

さて、すべてのベースをテスト[1, n)することは、大規模な計算時間において非常に高価になりますn。Miller-Rabinテストには、有限フィールドでランダムに選択された一部のベースのみをテストする確率的バリアントがあります。ただし、素数のaベースのみをテストするだけで十分であるため、効率的で決定論的な方法でテストを実行できることがわかりました。実際、すべての素数ベースをテストする必要があるわけではありません。特定の数だけが必要であり、その数は素数性をテストする値のサイズに依存します。

不十分な数の素数ベースがテストされると、テストは偽陽性を生成する可能性があります。テストがその複合性を証明できない場合、奇数の複合整数です。具体的には、基数aが奇数の合成数の合成性を証明できない場合、その数はbaseへの強い擬似素数と呼ばれaます。この課題は、Nth個の素数以下のすべての基底に対して強い擬似プライムである奇数の合成数を見つけることです(これは、th個の素数以下のすべての素数の基底に対して強い疑似素数であると言うことに相当しますN) 。


1
サンドボックスの投稿(現在削除済み)
Mego

1からのすべての奇数値をテストするアルゴリズムは、ルールで許可されている強力な疑似原始性を求めていますか?
user202729

@ user202729なぜそうならないのかわかりません。何だと思いますか?
Mego

ほとんどの回答は単純にブルートフォースであるため、これを最もコードの速い質問にすることをお勧めします。
ニールA.

@NeilA。私はこれが最速のコードとして優れていることに同意しません。(別のアルゴリズムがまだ開発されておらず、PPCGがそうすることを期待していないため)回答がほぼ確実にブルートフォースになることは事実ですが、コードゴルフははるかにシンプルで、エントリーの障壁がはるかに低くなります(提出者から)独自のソリューションを採点できます)、すべてのソリューションを実行して採点する必要はありません(そして法外なランタイムに対処します)。そして、この問題はゴルフチャレンジとして十分に興味深いものです。
メゴ

回答:


4

C、349 295 277 267 255バイト

N,i;__int128 n=2,b,o,l[999];P(m){i<N&&P(m<2?l[i++]=n:n%m?m-1:n++);}main(r){for(P(scanf("%d",&N));r|!b;)for(++n,b=i=N;i--&&b;){for(b=n-1,r=0;~b&1;b/=2)++r;for(o=1;b--;o=o*l[i]%n);for(b=o==1;r--;o=o*o%n)b|=o>n-2;for(o=r=1;++o<n;r&=n%o>0);}printf("%llu",n);}

stdinで1から始まる入力を受け取ります。例:

echo "1" | ./millerRabin

シーケンス内の新しい値をすぐに発見することは決してありませんが、仕事は完了します。更新:さらに遅くなりました!

  • ニールAの答え(a^(d*2^r) == (a^d)^(2^r))からインスピレーションを得て、少し速く、短く
  • この課題に対するすべての解決策が奇数になることを認識した後、再び大幅に遅くなるため、奇数のみをチェックすることを明示的に実施する必要はありません。
  • GCCを使用するようになりました__int128。これはunsigned long long、より大きな数で作業する場合よりも短いです。リトルエンディアンのマシンでも、printfは%llu引き続き正常に動作します。

縮小されていない

N,i;
__int128 n=2,b,o,l[999];
P(m){i<N&&P(m<2?l[i++]=n:n%m?m-1:n++);}
main(r){
    for(P(scanf("%d",&N));r|!b;)
        for(++n,b=i=N;i--&&b;){
            for(b=n-1,r=0;~b&1;b/=2)++r;
            for(o=1;b--;o=o*l[i]%n);
            for(b=o==1;r--;o=o*o%n)b|=o>n-2;
            for(o=r=1;++o<n;r&=n%o>0);
        }
    printf("%llu",n);
}

(期限切れ)内訳

unsigned long long                  // Use the longest type available
n=2,N,b,i,d,p,o,                    // Globals (mostly share names with question)
l[999];                             // Primes to check (limited to 999, but if you
                                    // want it to be "unlimited", change to -1u)
m(){for(o=1;p--;o=o*l[i]%n);}       // Inefficiently calculates (l[i]^p) mod n

// I cannot possibly take credit for this amazing prime finder;
// See /codegolf//a/5818/8927
P(m){i<N&&P(m<2?l[i++]=n:n%m?m-1:n++);}

main(r){
    for(
        P(scanf("%llu",&N));        // Read & calculate desired number of primes
        r|!b;){                     // While we haven't got an answer:
        n=n+1|1;                    // Advance to next odd number
        for(b=1,i=N;i--&&b;){       // For each prime...
            for(d=n-1,r=0;~d&1;d/=2)++r; // Calculate d and r: d*2^r = n-1
            // Check if there exists any r such that a^(d*2^r) == -1 mod n
            // OR a^d == 1 mod n
            m(p=d);
            for(b=o==1;r--;b|=o==n-1)m(p=d<<r);
            // If neither of these exist, we have proven n is not prime,
            // and the outer loop will keep going (!b)
        }
        // Check if n is actually prime;
        // if it is, the outer loop will keep going (r)
        for(i=r=1;++i<n;r&=n%i!=0);
    }
    printf("%llu",n);               // We found a non-prime; print it & stop.
}

前述のように、これは1ベースの入力を使用します。ただし、n = 0の場合、関連シーケンスhttps://oeis.org/A006945に従う9が生成されますもう違います; 今では0でハングします。

すべてのnで機能するはずです(少なくとも出力が2 ^ 64に達するまで)が、非常に遅いです。私は、n = 0、n = 1、および(多くの待機の後)n = 2で検証しました。


私は私の解決策に突破口を開いて、それからあなたは私を1つだけアップします...いいね!
ニールA.

@NeilA。ごめんなさい!更新を投稿する前に、短いint型で遊んでいました。ただし、2バイトはどこかにあるはずです。D:これは2つの異なる非ゴルフの言語だ考えると、驚くほど競争力が輩出される
デイブ

3

Python 2、633 465 435 292 282 275 256 247バイト

0インデックス付き

実装に疑問を呈し、新しいことを試してください

関数からプログラムに変換すると、なんらかのバイトが節約されます...

Python 2で同じことをするための短い方法が得られたら、Python 2を使用します。除算はデフォルトで整数であるため、2で除算するより簡単な方法でprintあり、括弧は必要ありません。

n=input()
f=i=3
z={2}
a=lambda:min([i%k for k in range(2,i)])
while n:
 if a():z|={i};n-=1
 i+=2
while f:
 i+=2;d,s,f=~-i,0,a()
 while~d&1:d/=2;s+=1
 for y in z:
  x=y**d%i
  if x-1:
   for _ in[]*s:
    x*=x
    if~x%i<1:break
   else:f=1
print i

オンラインでお試しください!

Pythonは他の言語と比較して遅いことで有名です。

絶対正しさのための試行分割テストを定義し、擬似素数が見つかるまでミラーラビンテストを繰り返し適用します。

オンラインでお試しください!

編集:最終的に答えをゴルフ

編集minトライアルディビジョンプライマリティテストに使用され、それをlambda。効率は劣りますが、短くなります。また、私自身を助けることができず、ビット長の演算子をいくつか使用しました(長さの違いはありません)。理論的には、(少し)速く動作するはずです。

編集:ありがとう@デイブ。私の編集者は私をしました。タブを使用していると思っていましたが、代わりに4つのスペースに変換されていました。また、ほぼすべてのPythonヒントをすべて調べて適用しました。

編集:0インデックスに切り替えて、素数を生成することで数バイトを節約できます。また、いくつかの比較を再考しました

編集for/elseステートメントの代わりにテストの結果を保存する変数を使用しました。

編集lambdaパラメータの必要性を排除するために、関数内を移動しました。

編集:バイトを節約するためにプログラムに変換

編集:Python 2はバイトを節約します!また、入力をに変換する必要はありませんint


あなたが処理した方法のために+1 a^(d*2^r) mod n
デイブ

あなたが実際には単一のスペース(または単一のタブ)を保存するにはPythonでインデント...バイトの全体の多く、使用することができることを知っている
デイブ

@デイブ:これは、インデントレベルごとに1つのタブを使用しています
ニールA.

IDEがあなたをいじり、タブを使用していることを伝えながらスペースを節約していると思います。それらを単一のスペースに置き換えると、バイト数はわずか311バイトになります!オンラインでお試しください!
デイブ

@デイブ:OK、それは奇妙です、ありがとう、答えを更新します。
ニールA.

2

Perl + Math :: Prime :: Util、81 + 27 = 108バイト

1 until!is_provable_prime(++$\)&&is_strong_pseudoprime($\,2..nth_prime($_));$_=""

-lpMMath::Prime::Util=:all(27バイトのペナルティ、痛い)で実行します。

説明

基本的にあらゆるものが組み込まれているのはMathematicaだけではありません。Perlには最初の大きなライブラリリポジトリの1つであるCPANがあり、このようなタスクのための既製のソリューションの膨大なコレクションがあります。残念ながら、それらはデフォルトではインポート(またはインストール)されていません。つまり、基本的にでそれらを使用するのは良い選択肢ではありませんが、そのうちの1つが問題に完全に適合する場合

素数ではないが、2からn番目の素数までのすべての整数基底に対する強い擬似素数が見つかるまで、連続する整数を実行します。コマンドラインオプションは、問題のビルトインを含むライブラリをインポートし、暗黙的な入力も設定します(一度に1行ずつMath::Prime::Util、整数の改行を嫌う独自のビルトインbignumライブラリを持っています)。これは、$\厄介な解析を減らし、暗黙的に出力を生成できるようにするために、変数として(出力行セパレーター)を使用する標準のPerlトリックを使用します。

is_provable_primeここでは、確率論的なプライムテストではなく、決定論的なプライムテストを要求する必要があることに注意してください。(特に、確率的素数テストが最初にMiller-Rabinを使用している可能性が高いことを考えると、この場合、信頼できる結果が得られるとは期待できません!)

Perl + Math :: Prime :: Util、71 + 17 = 88バイト、@ Dadaとの共同作業

1until!is_provable_prime(++$\)&is_strong_pseudoprime$\,2..n‌​th_prime$_}{

-lpMntheory=:all(17バイトのペナルティ)で実行します。

これは、私が知らなかったいくつかのPerlゴルフトリックを使用し(明らかにMath :: Prime :: Utilには省略形があります!)、使用は考えていませんでした(暗黙的にすべての行}{を出力するの$\ではなく、"$_$\"暗黙的に1回出力する) 、または知っていたが、どういうわけか間違って適用されました(関数呼び出しから括弧を削除)。これらを指摘してくれた@Dadaに感謝します。それ以外は同じです。


もちろん、ゴルフ風の言語が来て、他の言語に勝ります。よくやった!
ニールA.

ntheory代わりに使用できますMath::Prime::Util。また、}{代わりに;$_=""うまくいくはずです。また1、いくつかの関数呼び出しの後のスペースと括弧を省略できます。また、の&代わりに動作し&&ます。88バイトになるはずですperl -Mntheory=:all -lpe '1until!is_provable_prime(++$\)&is_strong_pseudoprime$\,2..nth_prime$_}{'
ダダ

私は完全に忘れていました}{。(奇妙なことに、括弧のことは覚えていましたが、Perlでゴルフをしてからしばらく経ち、それを除外する規則を思い出せませんでした。)ntheory略語についてはまったく知りませんでした。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.