あなたは本当に素数を見つけるのですか?

あなたは本当に素数を見つけるのですか?


素数の定義は、非常にシンプルに見える 数は1で割り切れるであり、それ自体、この数が素数である場合にだけ。

Lianqi

本当に私は恐れていない多くの人々できプライム関連の書き込み効率的なアルゴリズムを午前、素数の定義は単純であると考えてはいけません。たとえば、あなたがこのような関数を書いてみましょう:

// 返回区间 [2, n) 中有几个素数 
int countPrimes(int n)

// 比如 countPrimes(10) 返回 4
// 因为 2,3,5,7 是素数

どのようにこの関数を書くのでしょうか?私たちは書くべきだと思います。

int countPrimes(int n) {
    int count = 0;
    for (int i = 2; i < n; i++)
        if (isPrim(i)) count++;
    return count;
}

// 判断整数 n 是否是素数
boolean isPrime(int n) {
    for (int i = 2; i < n; i++)
        if (n % i == 0)
            // 有其他整除因子
            return false;
    return true;
}

時間のような言葉書かれた複雑 ザ・ n個 2 O(N ^ 2) 、大きな問題。まず、あなたがisPrimeのアイデアを支援する機能を使用するだけでは十分で効率的ではありません。

そして、あなたはisPrime機能を使用した場合でも、書き込みアルゴリズムも計算の冗長性に存在します。

セーバー

数はアルゴリズムの記述方法を、プライムでないかどうかを判断したい場合は、単に下、先着順。ただ、少し条件を入れ直すためisPrimコード上で変更します。

boolean isPrime(int n) {
    for (int i = 2; i * i <= n; i++)
        ...
}

換言すれば、iがnに横断する必要はない、とSQRT(N)のみが必要であることができます。なぜ、我々は、例を与えるとし、N = 12。

12 = 2 × 6
12 = 3 × 4
12 = sqrt(12) × sqrt(12)
12 = 4 × 3
12 = 6 × 2

2つの最初の二つの製品は、今度は、SQRT(N)における臨界点を逆転することが分かります。
[2、SQRT(N)]割り切れる因子のこの区間内に見出されている言い換えれば、我々は直接nが理由間隔で、素数であると結論することができる[SQRT(n)は、N]であることが見出されることはありません割り切れる要因。

さて、時間はisPrimeの複雑さの関数はO(SQRT(N))に減少しているが、我々はcountPrimesも使​​用されるなど、ので、より多くの読者はSQRT(N)の意味を理解していることだけを期待するよりも、この機能を必要としない、実際に機能を実現します。

大乗

効率的な実装countPrimes

この問題への効果的なソリューションのコア開発は、前方アンチ上記の従来の考え方であるとする:
まず、スタート2から、私たちは、その後、2×2 = 4、3×、2が素数であることを知っている 2 = 6、4×2 = 8 ... ありませんそれは素数かもしれません。
その後、我々はまた、3素数を発見し、その後、3×2 = 6、3× 3 = 9、3×4 = 12 ... も素数であるれていません。

あなたはそれの除外のロジックを少し理解していれば、ここを参照してください?私たちは、コードの最初のバージョンを見てみましょう。

int countPrimes(int n) {
    boolean[] isPrim = new boolean[n];
    // 将数组都初始化为 true
    Arrays.fill(isPrim, true);

    for (int i = 2; i < n; i++) 
        if (isPrim[i]) 
            // i 的倍数不可能是素数了
            for (int j = 2 * i; j < n; j += i) 
                    isPrim[j] = false;

    int count = 0;
    for (int i = 2; i < n; i++)
        if (isPrim[i]) count++;

    return count;
}

上記のコードは、あなたが理解できる場合は、全体的なアイデアをマスターしたが、最適化することができる2つのマイナーな分野があります。

高騰期間

まず、リコールはちょうど数がためのループ横断するのみ必要がある対称性要因による素数isPrime機能、であるかどうかを判断する[2,sqrt(n)]十分。ここでループのための私たちの外側には、似ています只需要遍历到 sqrt(n)

for (int i = 2; i * i < n; i++) 
    if (isPrim[i]) 
        ...

加えて、ほとんどforループ内で最適化することができる気付きません。我々の以前のアプローチは、次のとおりです。

for (int j = 2 * i; j < n; j += i) 
    isPrim[j] = false;

これは、順番に整数複数のIフラグが偽であることができ、冗長性の計算が残っています。

例えば、N = 25のため、私は、アルゴリズムマークを= 4×4×2 = 8.4×3 = 12番号などが、2つの図は、I = 2 iは2×4,3×4のマークの3 =されていますA。

私たちは少しを最適化し、私は開始* jはiの広場トラバーサルから、代わりの2から始めてみましょうことができます。

for (int j = i * i; j < n; j += i) 
    isPrim[j] = false;

このように、アルゴリズムの効率的な実装の一等地にカウントが、実際には、このアルゴリズムは、エラトステネスのふるいと呼ばれる、名前を持っています。完全な最終的なコードを見てください:

int countPrimes(int n) {
    boolean[] isPrim = new boolean[n];
    Arrays.fill(isPrim, true);
    for (int i = 2; i * i < n; i++) 
        if (isPrim[i]) 
            for (int j = i * i; j < n; j += i) 
                isPrim[j] = false;

    int count = 0;
    for (int i = 2; i < n; i++)
        if (isPrim[i]) count++;

    return count;
}

:アルゴリズムの時間複雑さは明らかにオペランドであるべきかについての2つの入れ子ループの時間で、より困難であると考えられる
。。。N- / N-2 + / + 3 N- / N - + 5/7 +···= N×( 1/2 + 1/3 + 1/5 + 1 / ... 7)
括弧内は相互素数です。最終的な結果は、 ザ・ N * リットル インクルード グラム リットル インクルード グラム N O(N * loglogN) 、関心のある読者は、アルゴリズムの複雑さが証明された時間をチェックすることができます。

公開された126元の記事 ウォン称賛57 ビュー90000 +

おすすめ

転載: blog.csdn.net/wolfGuiDao/article/details/104952718