素?cái)?shù)的計(jì)算 - 從試除到篩法

Cover

素?cái)?shù)的計(jì)算 - 從試除到篩法

昨天搜索素?cái)?shù)的問題的時(shí)候,找到一篇很棒的文章,轉(zhuǎn)載一下,并加上一些自己的理解。

文章鏈接: 素?cái)?shù)的計(jì)算: 從試除到篩法(C++實(shí)現(xiàn))

素?cái)?shù)定理:

素?cái)?shù)的個(gè)數(shù)是有規(guī)律的,對(duì)于正實(shí)數(shù)x,定義T(x)為素?cái)?shù)計(jì)數(shù)函數(shù):不大于x的素?cái)?shù)的個(gè)數(shù)。

那么會(huì)有:T(x) 約等于 x / ln(x) .

其中 T(x) / (x/ln(x)) 總是小于1.17。這個(gè)性質(zhì)能夠幫我們確定x以內(nèi)的最大素?cái)?shù)個(gè)數(shù),以分配存儲(chǔ)空間(即數(shù)組開多大)。

對(duì)于找不大于x的所有素?cái)?shù),有很多方法,下面從最簡(jiǎn)單的到最高效的介紹。

試除法

思想:

  1. 判斷方法:對(duì)于一個(gè)數(shù)n,將其分別除以[2,n]的每一個(gè)整數(shù),如果有可以整除的,則不是素?cái)?shù)。

  2. 循環(huán)所有不大于x的數(shù),確定數(shù)xi是否為素?cái)?shù)以求出不大于x的所有素?cái)?shù)。

算法實(shí)現(xiàn):

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>

using namespace std;

// 判斷n是否為素?cái)?shù)
bool is_prime(int n)
{
    if (n < 2)
        return false;
    for (int i = 2; i < n; ++i)
        if (n % i == 0)
            return false;
    return true;
}

// 計(jì)算所有不大于n的素?cái)?shù)
void get_prime(vector<int>& prime, int n)
{
    for(int i = 2; i <= n; ++i)
        if(is_prime(i)) // 判斷i是否是素?cái)?shù)
            prime.push_back(i);
}
int main()
{
    int n = 100000;
    vector<int> prime;
    get_prime(prime, n);
    return 0;
}

算法復(fù)雜度: O(n)

試除法優(yōu)化

思想:

對(duì)上面思想的分析,我們發(fā)現(xiàn),若有數(shù)n = x*y,那么y與x中必有一個(gè)滿足:k <= sqrt(n)。即在sqrt(n)之前沒有找到能夠整數(shù)n的數(shù),那么n之后也不會(huì)有,所以對(duì)于判斷n是否為素?cái)?shù)的方法中的循環(huán),只需要判斷到sqrt(n)即可。

算法實(shí)現(xiàn)

更改is_prime方法:


bool is_prime(int n)
{
    if (n < 2)
        return false;

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

算法復(fù)雜度: O(n * sqrt(n))

上面的都是暴力手法,下面介紹科學(xué)的藝術(shù)手法。

埃氏篩法

埃拉托斯特尼篩法,是古希臘數(shù)學(xué)家發(fā)明的計(jì)算素?cái)?shù)的方法(媽耶,古希臘就研究出來了)。

思想:

對(duì)于求解不大于n的素?cái)?shù):

  1. 找出不大于sqrt(n)內(nèi)的素?cái)?shù) p1、p2、p3 .... pn (1 <= n <= sqrt(k))

  2. 依次剔除不大于n的pi的倍數(shù)。

  3. 剩下的都是素?cái)?shù)。

這里有一張埃氏篩法的工作原理圖:

埃氏篩法

算法實(shí)現(xiàn):

void get_prime (vector<bool> &isPrime, int n) {
    isPrime.assign(n + 1, true);

    if (n < 2) return;

    for (int i = 2; i <= n; i++) {
        if (isPrime[i]) {
            // 若計(jì)算規(guī)模較大,加上 i < sqrt(n) 的判斷條件
            for (int j = i * i; j <= n && i < sqrt(n); j += i) {
                isPrime[j] = false;
            }
        }

    }
}

這里有一個(gè)原文沒有提到的點(diǎn),在我的實(shí)踐中,當(dāng) n > 46000 時(shí),會(huì)出現(xiàn) i*i 超出int范圍的問題,此時(shí)j會(huì)是負(fù)值,造成非法數(shù)組訪問的錯(cuò)誤。所以如果計(jì)算規(guī)模較大,加上 i < sqrt(n) 的判斷條件。

算法時(shí)間復(fù)雜度: O(nloglogn)

歐拉篩法

思想

埃氏篩法會(huì)對(duì)兩個(gè)素?cái)?shù)的公倍數(shù)多次剔除,根據(jù)這一問題優(yōu)化后,便出現(xiàn)了歐拉篩法:對(duì)于一個(gè)沒有被篩過的數(shù),只要被第一個(gè)數(shù)篩了就行了。

算法實(shí)現(xiàn)

void get_prime(vector<int> &prime, vector<bool> &isPrime, int n) {
    isPrime.assign(n + 1, true);
    int max_num = (n / log(n)) * 1.17 + 1;
    if (n < 2) return;

    for (int i = 2; i <= n; i++) {
        if (isPrime[i]) {
            prime.push_back(i);
        }
        for (int j = 0; j < isPrime.size() && i * prime[j] <= n; j++) {
            isPrime[i*prime[j]] = false;

            if (i % prime[j] == 0) break;
        }
    }
}

這個(gè)算法的精髓主要在于:

if (i % prime[j] == 0) break;

證明:對(duì)于一個(gè)數(shù)i,若能整除prime[j],那么有 i = a * prime[j]。當(dāng)判斷prime[j+1]能否被i整除時(shí),有 i * prime[j + 1] = a * prime[j] * prime[j+1],即此數(shù)(i*prime[j+1])已經(jīng)被prime[j]剔除過了,所以跳出。

此算法時(shí)間復(fù)雜度:O(n)

數(shù)組開多大

最前面已經(jīng)說了,數(shù)組開多大可以用T(x) / (x/ln(x)) <= 1.17 來決定,那么:

int max_num = (n / log(n)) * 1.17 + 1;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 什么是素?cái)?shù) 素?cái)?shù)又稱質(zhì)數(shù),是指大于1的自然數(shù)中,除了1和它本身,不能被其它自然數(shù)整除的數(shù)字。1被定義為非素?cái)?shù)。大于...
    程點(diǎn)閱讀 7,686評(píng)論 1 7
  • 在C語言中,五種基本數(shù)據(jù)類型存儲(chǔ)空間長(zhǎng)度的排列順序是: A)char B)char=int<=float C)ch...
    夏天再來閱讀 4,081評(píng)論 0 2
  • 【程序1】 題目:古典問題:有一對(duì)兔子,從出生后第3個(gè)月起每個(gè)月都生一對(duì)兔子,小兔子長(zhǎng)到第三個(gè)月后每個(gè)月又生一對(duì)兔...
    開心的鑼鼓閱讀 3,404評(píng)論 0 9
  • 計(jì)算機(jī)二級(jí)C語言上機(jī)題庫(南開版) 1.m個(gè)人的成績(jī)存放在score數(shù)組中,請(qǐng)編寫函數(shù)fun,它的功能是:將低于平...
    MrSunbeam閱讀 6,629評(píng)論 1 42
  • 我于早晨出門 古老的街道上空無一人 偶爾 被踩碎了的露珠會(huì)抱怨幾聲 醒來的花朵側(cè)耳聆聽 猜測(cè)著 這打破寧靜的...
    杜曉剛閱讀 314評(píng)論 0 1

友情鏈接更多精彩內(nèi)容