web-dev-qa-db-ja.com

n番目の素数の計算と出力

私は素数を計算しようとしています。ただし、n番目の素数(ユーザー入力)のみを計算して印刷し、残りの部分(印刷されない)を計算するときはn番目の素数のみが印刷されます。

ここに私がこれまでに書いたものがあります:

import Java.util.Scanner;
/**
 * Calculates the nth prime number
 * @author {Zyst}
 */
public class Prime {
    public static void main(String[] args) {

        Scanner input = new Scanner(System.in);
        int n, 
            i = 2, 
            x = 2;

        System.out.printf("This program calculates the nth Prime number\n");
        System.out.printf("Please enter the nth prime number you want to find: ");
        n = input.nextInt();

        for(i = 2, x = 2; i <= n; i++) {
            for(x = 2; x < i; x++) {
                if(i % x == 0) {
                    break;
                }
            }
            if(x == i) {
                System.out.printf("\n%d is prime", x);
            }
        }
    }
}

これは、1からnまでの素数を計算するために書いたプログラムです。ただし、n番目の素数のみを印刷したいので、

私が考えていたのは、ある種のcount intを作成し、素数を見つけるたびに++し、count == nのときにその数を出力することですが、どうすればよいかわかりません着陸。

34
Erick

N番目の素数を計算するために、2つの主要なバリアントを知っています。

簡単な方法

目的のnに達するまで、2から始まるすべての素数を数えることです。番目

これは、さまざまなレベルの洗練度と効率で実行できますが、概念的に異なる方法が2つあります。最初は

順番にすべての数の素数をテストする

これは、次のようなドライバー関数によって実現されます。

public static int nthPrime(int n) {
    int candidate, count;
    for(candidate = 2, count = 0; count < n; ++candidate) {
        if (isPrime(candidate)) {
            ++count;
        }
    }
    // The candidate has been incremented once after the count reached n
    return candidate-1;
}

そして、効率を決定する興味深い部分はisPrime関数です。

素数チェックの明確な方法は、1で割り切れる1より大きい数として素数を定義し、それ自体で学校で学んだことです¹。

裁判区分

定義をコードに直接変換すると、

private static boolean isPrime(int n) {
    for(int i = 2; i < n; ++i) {
        if (n % i == 0) {
            // We are naive, but not stupid, if
            // the number has a divisor other
            // than 1 or itself, we return immediately.
            return false;
        }
    }
    return true;
}

しかし、試してみるとすぐにわかるように、その単純さには遅さが伴います。その素数性テストでは、1000を見つけることができます番目 プライム、7919、数ミリ秒(私のコンピューターでは約20)で、10000番目 プライム、104729、数秒(〜2.4s)、100000番目 プライム、1299709、数分(約5)、100万番目のプライム、15485863、約8時間半、1000万番目のプライム、179424673、週などがかかります。実行時の複雑さは2次関数よりも劣ります-Θ(n²* log n)。

したがって、私たちは素数テストをいくらかスピードアップしたいと思います。多くの人がとるステップは、nの除数(n自体を除く)が最大でn/2になる可能性があるという認識です。その事実を使用して、試行除算ループをn/2ではなくn-1にのみ実行させる場合、アルゴリズムの実行時間はどのように変わりますか?合成数の場合、ループの下限は何も変更しません。素数の場合、試行分割の数は半分になるので、全体的に実行時間は2よりも少し小さくする必要があります。試してみると、実行時間がほぼ正確に半分になることがわかります素数よりも多くの複合体があるにもかかわらず、素数の素数の検証にほとんどすべての時間が費やされます

100万分の1の素数を見つけたい場合、それはあまり役に立たなかったので、もっと良くしなければなりません。ループの制限をさらに減らして、n/2の上限が実際に必要な数値を見てみましょう。 n/2nの約数である場合、n/2は整数です。つまり、nは2で割り切れます。しかし、ループは2を超えないため、(n = 4を除いて)n/2に達することはありません。ジョリーグッド、それでnの次に大きい可能な除数は何ですか?もちろん、n/3。ただし、n/3は、整数の場合、つまりnが3で割り切れる場合にのみnの除数になります。ループは3(またはその前、2)で終了し、n/3に到達しません(n = 9を除く)。次に大きい可能な除数...

ちょっと待って! 2 <-> n/23 <-> n/3があります。 nの約数はペアになります。

nの対応する除数のペア(d, n/d)を考慮すると、d = n/d、つまりd = √n、またはそれらの1つ、たとえばdが他方よりも小さくなります。しかし、その後d*d < d*(n/d) = nおよびd < √nnの対応する除数の各ペアには、(少なくとも)√nを超えないものが含まれます。

nが複合の場合、その最小の非自明な除数は√nを超えません。

したがって、ループ制限を√nに減らすことができ、それによりアルゴリズムの実行時の複雑さが軽減されます。これはΘ(n1.5 *√(log n))、しかし経験的には少し良くなるように見える-しかし、経験的な結果から信頼できる結論を引き出すのに十分なデータはありません。

それは約16秒で100万番目、9分弱で1000万番目を検出し、約4時間半で100万番目を検出します。それはまだ遅いですが、10年かそこらからは程遠いので、素朴な試験部門が必要になります。

323 = 17 * 19のように、素数の平方と2つの近い素数の積があるため、√nより下の試行除算ループの制限を減らすことはできません。したがって、トライアル部門にとどまりながら、アルゴリズムを改善する他の方法を今すぐ探す必要があります。

簡単にわかるのは、2以外の素数は偶数ではないということです。したがって、2を処理した後に奇数をチェックするだけで済みます。ただし、偶数は最も安価であるため、それほど違いはありません。合成-素数の素数性の検証にまだ多くの時間が費やされています。ただし、偶数を除数候補として見ると、nが偶数で割り切れる場合、n自体は偶数でなければならないため、(2を除いて)より大きい偶数による除算の前に複合として認識されます2回以上試行されます。したがって、アルゴリズムで発生する2より大きい偶数によるすべての除算では、必ずゼロ以外の剰余を残す必要があります。したがって、これらの除算を省略して、2と3から√nまでの奇数だけで分割可能性をチェックできます。これにより、数を素数または合成数として決定するために必要な除算の数が(完全に正確ではありませんが)半分になり、実行時間が短縮されます。それは良いスタートですが、私たちはもっと良くできますか?

別の大きな数のファミリは3の倍数です。実行する3分割ごとに3の倍数ですが、nがそれらの1つで割り切れる場合、3で割り切れるため、9、15、21で割り切れません。 、...アルゴリズムで実行すると、残りの0が残ります。これらの除算をどのようにスキップできますか? 2でも3でも割り切れない数値は、6*k ± 1という形式の数値です。 5から始まる(1より大きい数にのみ関心があるため)のは、5、7、11、13、17、19、...、2から4の間の1から次の代替へのステップです。簡単なので、使用できます

private static boolean isPrime(int n) {
    if (n % 2 == 0) return n == 2;
    if (n % 3 == 0) return n == 3;
    int step = 4, m = (int)Math.sqrt(n) + 1;
    for(int i = 5; i < m; step = 6-step, i += step) {
        if (n % i == 0) {
            return false;
        }
    }
    return true;
}

これにより、(ほぼ)1.5倍のスピードアップが得られるため、1億番目の素数まで約1時間半必要です。

このルートを続行する場合、次のステップは5の倍数の除去です。2、3、および5と互いに素な数は、フォームの数です。

30*k + 1, 30*k + 7, 30*k + 11, 30*k + 13, 30*k + 17, 30*k + 19, 30*k + 23, 30*k + 29

したがって、必要なのは、30個ごとの数字のうち8個(さらに3つの最小素数)で割るだけです。 7から始まる次から次へのステップは、4、2、4、2、4、6、2、6を順に実行します。これは、実装するのに十分簡単で、1.25倍の高速化を実現します。より複雑なコード)。さらに進むと、7の倍数が除去され、210の数字のうち48が除算され、11(480/2310)、13(5760/30030)などになります。倍数が除去された各プライムpは(ほぼ)p/(p-1)の高速化をもたらすため、戻り値は減少しますが、コスト(コードの複雑さ、ステップのルックアップテーブルのスペース)はプライムごとに増加します。

一般的に、6から7の素数の倍数(またはそれ以下)を除去した後、すぐに停止します。ただし、ここでは、すべての素数の倍数が除去され、素数のみが除数候補として残されている最後まで追跡できます。すべての素数を順番に見つけるので、各素数は、除数候補として必要になる前に見つけられ、将来の使用のために保存できます。これにより、アルゴリズムの複雑さが-誤って計算されていない場合-O(n1.5 /√(log n))。プライムを格納するためのスペース使用量を犠牲にします。

トライアル除算では、得られるのと同じくらい良いので、すべての素数で√nまたは最初の除算nに除算して、nの素数を決定する必要があります。ここでは約30分で1億番目の素数を見つけます。

じゃあどう?

高速素数テスト

素数には、合成数には通常ない非自明な除数がないこと以外の数論的性質があります。そのようなプロパティは、チェックが速い場合、確率的または決定論的な素数性テストの基礎を形成できます。そのような典型的な特性は、ピエール・ド・フェルマーの名前に関連付けられています。番目 世紀、それが見つかりました

pが素数の場合、pは(ap-a)すべてのa

これ-フェルマーのいわゆる「小さな定理」-は、同等の定式化で

pを素数にし、apで割り切れないようにします。その後、pp-1 -1。

広く普及している高速素数性テストのほとんどの基礎(例:Miller-Rabin)と、そのバリエーションまたは類似物(例:Lucas-Selfridge).

したがって、小さすぎない奇数nが素数かどうかを知りたい場合(偶数および小さい数は試行除算によって効率的に処理されます)、たとえばaの倍数ではない任意の数n(> 1)を選択できます2、およびnn-1 -1。n-1 巨大になります。これは、a^(n-1) ≡ 1 (mod n)であるかどうか、つまりモジュラーべき乗をチェックすることで最も効率的に行われます。その一致が成り立たない場合は、nが複合であることがわかります。ただし、それが成り立つ場合、nは素数、たとえば2^340 ≡ 1 (mod 341)であるが、341 = 11 * 31は複合であると結論付けることはできません。 a^(n-1) ≡ 1 (mod n)がベースnのFermat疑似プライムと呼ばれるような複合番号a

しかし、そのような発生はまれです。ベースa > 1が与えられた場合、ベースaには無限の数のFermat疑似プリムがありますが、それらは実際のプライムよりもはるかにまれです。たとえば、100000より下では78 base-2 Fermat擬似プライムと76 base-3 Fermat擬似プライムだけですが、9592プライムがあります。したがって、任意の奇数n > 1と任意のベースa > 1を選択し、a^(n-1) ≡ 1 (mod n)を見つけると、nが実際に素数である可能性が高くなります。

ただし、少し異なる状況にあり、nが与えられ、aのみを選択できます。それで、奇妙な複合nの場合、いくつのaの場合、1 < a < n-1a^(n-1) ≡ 1 (mod n)を保持できますか?あいにく、everyanと互いに素であるために合同が成立するような合成数-Carmichael数があります。つまり、Fermat検定で合成としてカーマイケル数を識別するには、nの素約数の1の倍数であるベースを選択する必要があります。そのような倍数は多くない場合があります。

ただし、Fermatテストを強化して、複合材料がより確実に検出されるようにすることができます。 pが奇数の素数である場合、p-1 = 2*mと記述します。次に、0 < a < pの場合、

a^(p-1) - 1 = (a^m + 1) * (a^m - 1)

pは、2つのファクターの1つを正確に除算します(2つのファクターは2異なるため、最大公約数は1または2です)。 mが偶数の場合、a^m - 1を同じ方法で分割できます。続けて、kが奇数のp-1 = 2^s * kの場合、書き込み

a^(p-1) - 1 = (a^(2^(s-1)*k) + 1) * (a^(2^(s-2)*k) + 1) * ... * (a^k + 1) * (a^k - 1)

その後、pは、因子の1つを正確に除算します。これにより、強力なフェルマー検定が行われ、

n > 2を奇数にします。 kを奇数にしてn-1 = 2^s * kを書き込みます。 1 < a < n-1を持つaが与えられた場合、

  1. a^k ≡ 1 (mod n)または
  2. 0 <= j < sを持つ任意のja^((2^j)*k) ≡ -1 (mod n)

nは、ベースastrong(Fermat)確率素数です。複合強塩基a(Fermat)の可能性のある素数は、基底aの強(Fermat)疑似素数と呼ばれます。強力なフェルマー擬似プライムは、1000000未満の通常のフェルマー擬似プライムよりもまれであり、78498個の素数、245 base-2 Fermat擬似プライム、および46 base-2の強いFermat擬似プライムしかありません。さらに重要なことに、奇数の複合nには、nが強力なFermat疑似プライムである(n-9)/4基底が最大で1 < a < n-1個あります。

したがって、nが奇数の複合である場合、nが1からn-1(排他的境界)の間でランダムに選択された基底を使用してk強いFermatテストに合格する確率は1/4^k未満です。

強力なフェルマーテストはO(log n)ステップを取り、各ステップはO(log n)ビットと1つまたは2つの数値の乗算を含むため、複雑さは単純な乗算を伴うO((log n)^ 3)です[巨大なnの場合、より洗練された乗算アルゴリズムは価値があります]。

Miller-Rabin検定は、ランダムに選択された基底を使用したk倍の強いFermat検定です。これは確率的なテストですが、十分に小さい境界の場合、決定論的な結果をもたらす短い組み合わせの塩基が知られています。

強力なフェルマーテストは、決定論的なAPRCLテストの一部です。

除算は比較的安価であり、ほとんどの複合材料を除外するため、最初のいくつかの小さな素数による試行除算をそのようなテストの前に行うことをお勧めします。

nを見つける問題について番目 素数、素数のすべての数のテストが実行可能な範囲で、複数の強いフェルマーテストを正しくする基底の既知の組み合わせがあるため、より高速になります-O(n *(log n)4)-アルゴリズム。

n < 2^32の場合、基数2、7、および61で素数性を検証できます。それを使用すると、約6分で1億番目の素数が見つかります。

素数除数によるエラトステネスのふるいによる複合材料の排除

順番に数字を調べて、それぞれが最初から素数であるかどうかをチェックする代わりに、関連する数字のセット全体を1つのピースとみなし、与えられた素数の倍数を一度に削除することもできます。これはエラトステネスのふるいとして知られています:

Nを超えない素数を見つけるには

  1. 2からNまでのすべての数値のリストを作成します
  2. 2からkの各Nに対して:kがまだ交差していない場合、それは素数です。 kのすべての倍数を複合としてクロス

素数は、リスト内の交差しない数字です。

このアルゴリズムはトライアル除算とは根本的に異なりますが、フェルマーテストやその他の素数のプロパティを使用する同様のテストとは対照的に、両方とも素数の可分性特性を直接使用します。

試行除算では、各数値nは、√nの小さい方とnの最小素数を超えないすべての素数とペアになります。ほとんどの複合材料は素数の除数が非常に小さいため、複合材料の検出はここでは平均して安価です。ただし、√nの下には比較的多くの素数があるため、素数のテストには費用がかかります。素数よりも多くの複合体がありますが、素数のテストのコストが非常に高いため、全体の実行時間を完全に支配し、試行分割を比較的遅いアルゴリズムにします。 Nより小さいすべての数値の試算はO(N1.5 /(log N)²)ステップ。

ふるいでは、各複合nは、そのすべての素因数とペアになりますが、onlyはそれらとのみです。したがって、素数は安価な数字であり、一度しか見られませんが、複合体はより高価であり、複数回交差しています。ふるいには「安い」数字よりも「高価な」数字が多く含まれているため、全体的に悪いアルゴリズムになると考えられるかもしれません。ただし、合成数には明確な素数の数が多くありません-nの別個の素数の数はlog nで区切られていますが、通常はmuchより小さく、数の平均数値<= nの異なる素数の除数はlog log nであるため、ふるいの「高価な」数値でさえ、試行分割の「安価な」数値よりも平均して高くない(またはほとんど高くない)。

最大のNをふるい、各素数のpに対して、交差するΘ(N/p)倍数があるため、交差の合計数はΘ(∑ (N/p)) = Θ(N * log (log N))です。これにより、Nまでの素数を見つけるためのアルゴリズムが、より高速な素数性テストを使用した試行除算または順次テストよりもmuch高速になります。

ただし、sieveには不利な点があり、O(N)メモリを使用します。 (ただし、セグメント化されたふるいを使用すると、時間の複雑さを増すことなくO(√N)に減らすことができます。)

nを見つけるために番目 素数は、Nまでの素数ではなく、ふるいがどこまで到達するかが事前にわからないという問題もあります。

後者は素数定理を使用して解決できます。 PNTによると

π(x) ~ x/log x (equivalently: lim π(x)*log x/x = 1),

ここで、π(x)xを超えない素数の数です(以下、logは自然対数である必要があります。アルゴリズムの複雑さのために、対数にどの基底が選択されるかは重要ではありません)。それから、p(n) ~ n*log nになります。ここで、p(n)nです。番目 素数であり、特に深い分析からわかっているp(n)の適切な上限があります。

n*(log n + log (log n) - 1) < p(n) < n*(log n + log (log n)), for n >= 6.

そのため、これをふるいの制限として使用でき、ターゲットをはるかに超えません。

O(N)スペースの要件は、セグメント化されたシーブを使用することで克服できます。その後、O(√N / log N)のメモリ消費のために√Nの下の素数を記録し、長さが増加するセグメント(ふるいがNに近い場合はO(√N))を使用できます。

上記のアルゴリズムには、いくつかの簡単な改善点があります。

  1. ではなく、2*pでのみpの倍数の交差を開始します
  2. ふるいから偶数を排除する
  3. ふるいからさらに小さい素数の倍数を排除する

これらはどれもアルゴリズムの複雑さを軽減しませんが、それらはすべて定数係数を大幅に削減します(試行除算の場合と同様に、pの倍数を削除すると、pが大きくなるほど速度が遅くなりますが、pが小さくなるとコードの複雑さが増します)。

最初の2つの改善を使用すると、歩留まりが向上します

// Entry k in the array represents the number 2*k+3, so we have to do
// a bit of arithmetic to get the indices right.
public static int nthPrime(int n) {
    if (n < 2) return 2;
    if (n == 2) return 3;
    int limit, root, count = 1;
    limit = (int)(n*(Math.log(n) + Math.log(Math.log(n)))) + 3;
    root = (int)Math.sqrt(limit) + 1;
    limit = (limit-1)/2;
    root = root/2 - 1;
    boolean[] sieve = new boolean[limit];
    for(int i = 0; i < root; ++i) {
        if (!sieve[i]) {
            ++count;
            for(int j = 2*i*(i+3)+3, p = 2*i+3; j < limit; j += p) {
                sieve[j] = true;
            }
        }
    }
    int p;
    for(p = root; count < n; ++p) {
        if (!sieve[p]) {
            ++count;
        }
    }
    return 2*p+1;
}

約18秒で1億番目の素数2038074743を見つけます。メモリ使用量を減らすとキャッシュの局所性が向上するため、booleansとしてではなく、フラグごとに1ビットを詰めてフラグを格納することで、この時間を約15秒(ここではYMMV)に短縮できます。

フラグをパックし、3の倍数も削除し、ビットトゥイドリングを使用してより高速なカウントを行い、

// Count number of set bits in an int
public static int popCount(int n) {
    n -= (n >>> 1) & 0x55555555;
    n = ((n >>> 2) & 0x33333333) + (n & 0x33333333);
    n = ((n >> 4) & 0x0F0F0F0F) + (n & 0x0F0F0F0F);
    return (n * 0x01010101) >> 24;
}

// Speed up counting by counting the primes per
// array slot and not individually. This yields
// another factor of about 1.24 or so.
public static int nthPrime(int n) {
    if (n < 2) return 2;
    if (n == 2) return 3;
    if (n == 3) return 5;
    int limit, root, count = 2;
    limit = (int)(n*(Math.log(n) + Math.log(Math.log(n)))) + 3;
    root = (int)Math.sqrt(limit);
    switch(limit%6) {
        case 0:
            limit = 2*(limit/6) - 1;
            break;
        case 5:
            limit = 2*(limit/6) + 1;
            break;
        default:
            limit = 2*(limit/6);
    }
    switch(root%6) {
        case 0:
            root = 2*(root/6) - 1;
            break;
        case 5:
            root = 2*(root/6) + 1;
            break;
        default:
            root = 2*(root/6);
    }
    int dim = (limit+31) >> 5;
    int[] sieve = new int[dim];
    for(int i = 0; i < root; ++i) {
        if ((sieve[i >> 5] & (1 << (i&31))) == 0) {
            int start, s1, s2;
            if ((i & 1) == 1) {
                start = i*(3*i+8)+4;
                s1 = 4*i+5;
                s2 = 2*i+3;
            } else {
                start = i*(3*i+10)+7;
                s1 = 2*i+3;
                s2 = 4*i+7;
            }
            for(int j = start; j < limit; j += s2) {
                sieve[j >> 5] |= 1 << (j&31);
                j += s1;
                if (j >= limit) break;
                sieve[j >> 5] |= 1 << (j&31);
            }
        }
    }
    int i;
    for(i = 0; count < n; ++i) {
        count += popCount(~sieve[i]);
    }
    --i;
    int mask = ~sieve[i];
    int p;
    for(p = 31; count >= n; --p) {
        count -= (mask >> p) & 1;
    }
    return 3*(p+(i<<5))+7+(p&1);
}

約9秒で1億番目の素数を見つけます。これは耐え難いほど長くはありません。

他のタイプのプライムシーブがあり、特に興味深いのはアトキンのふるいです。これは、(有理数)プライムの特定の合同クラスが、ofのいくつかの二次拡張の代数整数のリングの複合体であるという事実を利用します。ここに数学理論を展開する場所はありません、アトキンのふるいはエラトステネスのふるいよりもアルゴリズムの複雑さが低く、したがって大きな制限に適していると言えば十分です(小さな制限の場合、過度に最適化されていないアトキンのふるいは高いオーバーヘッドのため、同等に最適化されたエラトステネスのふるいよりも遅くなります)。 D. J.バーンスタインの primegen ライブラリ(Cで記述)は、2未満の数値に対して最適化されています。32 約1.1秒で1億番目の素数(ここ)を見つけます。

速い方法

nのみを検索する場合番目 素数、すべてのより小さい素数を見つけることにも本質的な価値はありません。それらのほとんどをスキップできる場合、多くの時間と作業を節約できます。 nの適切な近似_a(n)が与えられた場合番目 素数p(n)、素数π(a(n))a(n)を超えない素早い数を計算する方法がある場合、a(n)a(n)の間の欠落または過剰な素数を特定するために、p(n)の上下の小さな範囲をふるいにかけることができます。

上記のp(n)の簡単に計算されたかなり良い近似を見てきました。

a(n) = n*(log n + log (log n))

例えば。

π(x)を計算する良い方法は Meissel-Lehmer method で、これはおおよそπ(x)時間でO(x^0.7)を計算します(正確な複雑さは実装に依存し、Lagarias、Miller、Odlyzko、Deleglise、Rivatによる改良が可能です) O(xでπ(x)を計算する2/3 /log²x)時間)。

簡単な近似a(n)から始めて、e(n) = π(a(n)) - nを計算します。素数定理により、a(n)の近くの素数の密度は約1/log a(n)であるため、p(n)b(n) = a(n) - log a(n)*e(n)の近くになると予想され、log a(n)*e(n)よりわずかに大きい範囲をふるいにかけます。 p(n)がふるいにかけられた範囲にあるという確信を高めるために、範囲を2倍に増やすことができます。範囲が大きすぎると思われる場合は、b(n)の代わりに、より適切な近似a(n)を反復処理して、π(b(n))およびf(n) = π((b(n)) - nを計算できます。通常、|f(n)||e(n)|よりもはるかに小さくなります。 f(n)がおよそ-e(n)である場合、c(n) = (a(n) + b(n)) / 2p(n)により近い近似になります。 f(n)e(n)に非常に近い(そして0に非常に近い)という非常にまれなケースでのみ、p(n)の計算に匹敵する時間で最終のふるい分け段階を実行できる、π(a(n))に十分に近い近似を見つけることが問題になります。

一般に、初期近似の1つまたは2つの改善の後、ふるいにかけられる範囲は、ふるいの段階がO(n ^ 0.75)以上の複雑さを持つのに十分小さいです。

この方法は、約40ミリ秒で1億番目の素数を見つけ、10128番目のプライムである29996224275833。


tl; dr:nの検索番目 プライムは効率的に実行できますが、効率が高いほど、より多くの数学が関係します。


Java議論されたアルゴリズムのほとんどのコードは準備されています こちら 、誰かがそれらをいじりたい場合に備えています。


¹過度に興味のある魂に対する発言はさておき:現代の数学で使用される素数の定義は異なり、はるかに一般的な状況に適用できます。私たちが負の数を含むように学校の定義を適応させた場合-数字が1でも-1でもなければ素数であり、1、-1自体とその負数で割り切れる-これは(整数について)今日irreducibleofの要素。ただし、整数の場合、素数要素と既約要素の定義は一致します。

163
Daniel Fischer
int counter = 0;

for(int i = 1; ; i++) {
    if(isPrime(i)
        counter++;

    if(counter == userInput) {
        print(i);
        break;
    }
}

編集:あなたの主要な機能は少し作業を使用する可能性があります。ここに私が書いたものがあります:

private static boolean isPrime(long n) {
    if(n < 2)
        return false;

    for (long i = 2; i * i <= n; i++) {
        if (n % i == 0)
            return false;
    }
    return true;
}

注-要因を見るときはsqrt(n)に上がるだけなので、i * i <= n

5
ggrigery

あなたはメインメソッドでやり過ぎです。これをより管理しやすい部分に分割する必要があります。数値が素数の場合はtrueを返し、それ以外の場合はfalseを返すメソッドboolean isPrime(int n)を記述します。次に、isPrimeを使用するようにメインメソッドを変更します。

5
kevin cline

Java.math.BigIntegerにはnextProbablePrime()メソッドがあります。私はこれが暗号化のためのものであると推測していますが、あなたは仕事にそれを使用することができます。

BigInteger prime = BigInteger.valueOf(0);
for (int i = 0; i < n; i++) {
    prime = prime.nextProbablePrime();
}
System.out.println(prime.intValue());
4
Adam

私はあなた自身の思考過程で行方不明の行を追加しました。

static int nthPrimeFinder(int n){

    int counter = 1; // For 1 and 2. assuming n is not 1 or 2.
    int i = 2;
    int x = 2;
    int tempLength = n;

    while (counter <= n) {
        for (; i <= tempLength; i++) {
            for (x = 2; x < i; x++) {
                if (i % x == 0) {
                    break;
                }
            }
            if (x == i && counter < n) {
                //System.out.printf("\n%d is prime", x);
                counter++;
                if (counter == n) {
                    System.out.printf("\n%d is prime", x);
                    return counter;
                }
            }
        }
        tempLength = tempLength+n;
    }
    return 0;
}
0
Narita
public class prime{
    public static void main(String ar[])
    {
      int count;
      int no=0;
      for(int i=0;i<1000;i++){
        count=0;
        for(int j=1;j<=i;j++){

        if(i%j==0){
          count++;
         }
        }
        if(count==2){
          no++;
          if(no==Integer.parseInt(ar[0])){
            System.out.println(no+"\t"+i+"\t") ;
          }
        }
      }
    }
}
0
shravankumar

多くの正解と非常に詳細な回答を受け取っていることがわかります。非常に大きな素数についてはテストしていないと思います。そして、あなたの唯一の懸念は、あなたのプログラムが中間の素数を印刷するのを避けることです。

プログラムを少し変更するだけでうまくいきます。

ロジックを同じように保ち、printステートメントをループの外側に引き出します。 n個の素数の後に外側のループを解除します。

import Java.util.Scanner;
/**
 * Calculates the nth prime number
 * @author {Zyst}
 */
public class Prime {
    public static void main(String[] args) {

        Scanner input = new Scanner(System.in);
        int n, 
            i = 2, 
            x = 2;

        System.out.printf("This program calculates the nth Prime number\n");
        System.out.printf("Please enter the nth prime number you want to find:");
        n = input.nextInt();

        for(i = 2, x = 2; n > 0; i++) {
            for(x = 2; x < i; x++) {
                if(i % x == 0) {
                    break;
                }
            }
            if(x == i) {
                n--;
            }
        }
        System.out.printf("\n%d is prime", x);

    }
}
0
Nitesh

このプログラムは効率的です。数値の平方根を取得するためにもう1つのチェックインを追加し、それが割り切れるかどうか、それが素数でないかどうかを確認しました。これにより、すべての問題が効率的に解決されます。

public static void main(String[] args) {

            Scanner sc = new Scanner(System.in);
        int T; // number of test cases
        T = sc.nextInt();
        long[] number = new long[T];
        if(1<= T && T <= 30){
        for(int i =0;i<T;i++){
            number[i]=sc.nextInt(); // read all the numbers
        }
        for(int i =0;i<T;i++){
            if(isPrime(number[i]))
                System.out.println("Prime");
            else
               System.out.println("Not prime");    
        }
    }
    else
      return;
    }
    // is prime or not
    static boolean isPrime(long num){
        if(num==1)
          return false;
        if(num <= 3)
          return true;
        if(num % 2 == 0 || num % 3 == 0 || num % (int)Math.sqrt(num) == 0)
          return false;  
        for(int i=4;i<(int)Math.sqrt(num);i++){
            if(num%i==0)
              return false;
        }
       return true;     
    }
0

多くの正確かつ詳細な説明が利用可能ですが。しかし、ここに私のC実装があります:

#include<stdio.h>
#include<conio.h> 

main()
{
int pk,qd,am,no,c=0;
printf("\n Enter the Number U want to Find");
scanf("%d",&no);
for(pk=2;pk<=1000;pk++)
{
am=0;
for(qd=2;qd<=pk/2;qd++)
{
if(pk%qd==0)
{
am=1;
break;
}}
if(am==0)
c++;
if(c==no)
{
printf("%d",pk);
break;
}}
getch();
return 0;
}
0