1. 程式人生 > 實用技巧 >物件的繼承,super(),子類與父類的引數

物件的繼承,super(),子類與父類的引數

質數篩

閒談

原因

蕪湖,蒟蒻的第十篇部落格。(\(NOIP\)加油!!!)

背景

之前一直很想學習這裡但是沒有抽出時間,今天身體不適待在家中,就趁機學習了一下。

質數篩

背景

我們在資訊競賽的題目當中,很多時候會看到和質數相關的問題,我們如果用傳統的遍歷法的話,時間複雜度為\(O(\sqrt{n})\),並且一次只能求解一個。

bool isprime(int n){
    for(int i = 2;i <= sqrt(n); i++)
        if(n % i == 0) return false;
    return true;
}

但我們往往會遇到求解一個區間內的質數總數的時候,這時候往往會造成太大的時間複雜度,質數篩就是一種可以將時間複雜度降低為線性的優秀方法。

理解

首先我們來了解一下什麼叫做質數篩,首先我們開一個\(bool\)型別的陣列,將它的大小定義為我們所需要求範圍中的最大值,並將它們都賦值為真\(is_prime[maxn] = true;\),這個陣列中的元素如果為\(true\)那麼說明這個數為質數,如果為\(false\)那麼說明便不為質數,如何將合數找出來便非常的關鍵了。

埃拉託斯特尼篩法

這個過程是這樣實現的,首先從\(1\)開始,\(1\)既不是質數也不是合數,所以標記為\(false\);接著是\(2\)\(2\)是質數,便標記為\(true\),注意\(2\)\(maxn\)中所有能被\(2\)整除的數都標記為\(false\)

;接著是\(3\),標記為\(true\),將到\(maxn\)能被\(3\)整除的數標記為\(false\);接下來,我們發現\(4\)已經被標記為\(false\)了,所以我們跳過它,將沒有被標記過的\(5\)標記為\(true\),將到\(maxn\)能被\(5\)整除的數標記為\(false\);並一直做下去,就會把不超過\(false\)的合數全部標記為\(0\),不超過\(maxn\)的質數全部標記為\(true\)。因為這個過程最開始是希臘人是把數寫在塗臘的板上,每要劃去一個數,就在上面記以小點,尋求質數的工作完畢後,這許多小點就像一個篩子,並且是由埃拉託斯特尼發明的,所以叫做“埃拉託斯特尼篩法”,簡稱“篩法”。

int prime[MAXN];      //素數陣列
bool isprime[MAXN + 10];      //is_prime[i]表示i是素數
//返回n以內素數的個數
int solve(int n){
    int p = 0;      //素數個數計數器
    for (int i = 0; i <= n; i++)
        is_prime[i] = true;
    is_prime[0] = is_pri[1] = false;      //首先標記0和1不是素數
    for (int i = 2; i <= n; i++){
        if (is_prime[i]){      //如果i是素數
            prime[++p] = i;      //將素數放進素數表
            for (int j = 2 * i; j <= n; j += i)      //所有i的倍數都不是素數
                is_prime[j] = false;
        }
    }
    return p;
}

但是我們會發現,例如\(6\)這個數字,會被\(2\)\(3\)重複篩去一遍,會造成不必要的計算,時間複雜度是\(O(nlognlogn)\)所以我們將這個方法進行了改進。

尤拉篩

尤拉篩的核心是:讓每一個合數被最小質因數篩去。我們來看一個具體的例子。

\[\large{2\quad3\quad4\quad5\quad6\quad7\quad8\quad9\quad10\quad11\quad12\quad13\quad14\quad15\quad16} \]

\[primes:() \]

這次我們還運用了一個\(**質數表**\)prime[maxn]$來維護,首先把\(2\)把它加入到質數表當中,並在原陣列中刪除;

\[\large{\color{blue}2\quad3\quad4\quad5\quad6\quad7\quad8\quad9\quad10\quad11\quad12\quad13\quad14\quad15\quad16} \]

\[primes:(2) \]

並且用\(2\)來乘質數表中的每一個數,這裡只有\(2\),所以得出的是\(4\),所以我們將\(4\)在原陣列中劃去;

\[\large{\color{blue}2\quad3\quad\color{red}4\quad5\quad6\quad7\quad8\quad9\quad10\quad11\quad12\quad13\quad14\quad15\quad16} \]

\[primes:(2) \]

接下來是\(3\),我們也同樣將 它加入到質數表中,並在原陣列中刪除;

\[\large{\color{blue}2\quad\color{blue}3\quad\color{red}4\quad5\quad6\quad7\quad8\quad9\quad10\quad11\quad12\quad13\quad14\quad15\quad16} \]

\[primes:(2, 3) \]

同時與質數表中的每一個數字相乘,得到\(6\)\(9\),將它們劃掉;

\[\large{\color{blue}2\quad\color{blue}3\quad\color{red}4\quad5\quad\color{red}6\quad7\quad8\quad\color{red}9\quad10\quad11\quad12\quad13\quad14\quad15\quad16} \]

\[primes:(2, 3) \]

接下來是\(4\)(注意\(4\)也是需要遍歷的,只是不加入到質數表當中而已),我們劃掉\(8\),但是不劃掉\(12\),因為我們說尤拉篩的核心是讓每一個合數被最小的質因數篩去,應該用\(2\)去篩去\(12\)

\[\large{\color{blue}2\quad\color{blue}3\quad\color{red}4\quad5\quad\color{red}6\quad7\quad\color{red}8\quad\color{red}9\quad10\quad11\quad12\quad13\quad14\quad15\quad16} \]

\[primes:(2, 3) \]

我們分析,對於一個數\(x\),我們遍歷到質數表中的\(p\)時,如果發現\(p|x\),那麼我們就應該停止遍歷整個質數表。我們來證明一下:設\(x = pr(r \geq p)\)(\(p\)\(x\)的最小質因數),那麼對於任意的\(p' > p\),有\(p'x = pp'r = p(p'r)\),即說明\(p'x\)的最小質因數不是\(p'\),不應該在這裡劃掉。
按照這個思路我們可以得到。

\[\large{\color{blue}2\quad\color{blue}3\quad\color{red}4\quad\color{blue}5\quad\color{red}6\quad\color{blue}7\quad\color{red}8\quad\color{red}9\quad\color{red}{10}\quad\color{blue}{11}\quad\color{red}{12}\quad\color{blue}{13}\quad\color{red}{14}\quad\color{red}{15}\quad\color{red}{16}} \]

\[primes:(2, 3, 5, 7, 11, 13) \]

程式碼如下

int cnt = 0;
bool isprime[MAXN];
int primes[MAXN];
void solve_plus(int n){
    for (int i = 2; i <= n; i++){
        if (!isprime[i])
            primes[++cnt]  = i;
        for (int p = 1; p <= cnt; p++){
            if (p * i > n)
                break;
            isprime[p * i] = 1;
            if (i % p == 0)
                break;
        }
    }
}
那麼這就是我們在資訊競賽中最常用到的兩種質數篩了,多多敲程式碼更有助於理解。
完結撒花ヾ(✿゚▽゚)ノ