データ構造とアルゴリズムのインタビューの質問: 負でない整数 m と n が与えられた場合、m 以下の数値の中の素数の数を計算します。(ヒント: アルゴリズムの原理はエッシーふるい、線形ふるいです)

データ構造とアルゴリズムのインタビューの質問: 負でない整数 m と n が与えられた場合、m 以下の数値の中の素数の数を計算します。(ヒント: アルゴリズムの原理はエッシーふるい、線形ふるいです)

はじめに: データ構造とアルゴリズムのインタビューの質問: 負でない整数 m と n が与えられた場合、m 以下の数の中の素数の数を計算します。(ヒント: アルゴリズムの原理はエッシーふるい、線形ふるいです)

アルゴリズムのアイデア

アルゴリズムのアイデア:

質問の意味によれば、この質問は m 以下の素数の数を計算する必要があります。まず、整数が素数かどうかを判定し、素数の数を累積する必要があります。

素数を判定する最も一般的な方法は試行除算であり、n が素数かどうかを判定したいとすると、2 から n-1 までを除算するだけで済みます。 1 とそれ自体の場合、n は素数ではありません; それ以外の場合、n は素数です。ただし、この方法で必要な時間計算量 O(n) は非常に高く、実際のニーズを満たすことができません。また、効率を最大限に高めるために、エスペラント語ふるいまたは線形ふるいを使用して素数を見つけることができます。

エスペラント語のふるい:

1 から m までの各数値を列挙し、前の数値でふるいにかけられているかどうかを判断し、ふるい分けされていない場合は、その数値の倍数をすべて合成数(ふるいにかけられた)としてマークします。実装する際、素数をコンテナに入れることができ、合成数をふるい分ける際にスクリーニングされた素数をスキップできるため、効率が向上し、時間計算量は O ( nloglogn ) O(nloglogn) になりますO ( nログログn ) _ _ _ _

リニアふるい:

エスペラント語のふるいに似ていますが、ふるい分けの際に小さな素数を使って合成数をふるい分けし、ふるいにかけられなかった残りの数値が素数になります。たとえば、最初は 2 の倍数が合成数としてマークされ、2 回目は 3 の倍数が合成数としてマークされます。ただし、合成数は複数の素数によってスクリーニングされる可能性があるため、各数値は 1 回しかマークできないことに注意してください。時間計算量はO ( n ) O(n)O ( n )

線形ふるい法の方がより効果的に機能するため、線形ふるいアルゴリズムの実装を以下に示します。

#include <iostream>
#include <vector>

using namespace std;

int countPrimes(int n) {
    vector<bool> is_prime(n, true); // 初始化所有数都是质数
    vector<int> primes;             // 存储找到的质数

    for (int i = 2; i < n; ++i) {
        if (is_prime[i]) {         // 如果当前数仍然是质数
            primes.push_back(i);   // 将其加入质数数组
        }
        for (int j = 0; j < primes.size() && i * primes[j] < n; ++j) {
            // 遍历已有的质数进行筛选
            is_prime[i * primes[j]] = false;
            if (i % primes[j] == 0) break; // 当前数的质因子已经被筛选过了
        }
    }

    return primes.size();
}

int main() {
    int n = 10;
    int cnt = countPrimes(n);
    cout << cnt << endl; // 4

    return 0;
}

これはis_prime[i]数字iiを表しますi が素数かどうかに関係なくtrue2 から始まる各数値を列挙し、素数の場合は素数配列に追加し、その合成数をすべて除外します。特定の実装では、現在の数値が素数であることがわかっている場合、その素数を使用してより大きな合成数をスクリーニングすることができます。スクリーニング用の合成数を正確かつ迅速に見つけるために、既存の素数をすべて保存する素数の配列を維持します。各数字iiiでは、既存の素数をたどり、その倍数を 1 つずつ削除します。素因数がn \sqrt nn 、その倍数はnn未満でなければなりませんnなので、アルゴリズムはその倍数を反復する必要がありません。最後に、素数を出力できます。

  • Javaのバージョン
import java.util.ArrayList;
import java.util.List;

public class Main {
    
    

    public static int countPrimes(int n) {
    
    
        boolean[] isPrime = new boolean[n];
        List<Integer> primes = new ArrayList<>();
        for (int i = 2; i < n; ++i) {
    
    
            if (!isPrime[i]) {
    
    
                primes.add(i);
            }
            for (int j = 0; j < primes.size() && i * primes.get(j) < n; ++j) {
    
    
                isPrime[i * primes.get(j)] = true;
                if (i % primes.get(j) == 0) break;
            }
        }
        return primes.size();
    }

    public static void main(String[] args) {
    
    
        int n = 10;
        int cnt = countPrimes(n);
        System.out.println(cnt); // 4
    }
}

おすすめ

転載: blog.csdn.net/qq_51447496/article/details/131170917