你真的會求素數嗎?

2021-10-04 02:42:42 字數 2523 閱讀 2115

素數的定義看起來很簡單,如果乙個數如果只能被 1 和它本身整除,那麼這個數就是素數。

// 返回區間 [2, n) 中有幾個素數 

intcountprimes

(int n)

// 比如 countprimes(10) 返回 4

// 因為 2,3,5,7 是素數

int

countprimes

(int n)

// 判斷整數 n 是否是素數

boolean isprime

(int n)

這樣寫的話時間複雜度 o(n

2)

o(n^2)

o(n2

),問題很大。首先你用 isprime 函式來輔助的思路就不夠高效;

而且就算你要用 isprime 函式,這樣寫演算法也是存在計算冗餘的。

先來簡單說下如果你要判斷乙個數是不是素數,應該如何寫演算法。只需稍微修改一下上面的 isprim **中的 for 迴圈條件:

boolean isprime

(int n)

換句話說,i 不需要遍歷到 n,而只需要到 sqrt(n) 即可。為什麼呢,我們舉個例子,假設 n = 12。

12

=2 × 612=

3 × 412=

sqrt(12

) × sqrt(12

)12=4 × 312=

6 × 2

可以看到,後兩個乘積就是前面兩個反過來,反轉臨界點就在 sqrt(n)。

換句話說,如果在 [2,sqrt(n)] 這個區間之內沒有發現可整除因子,就可以直接斷定 n 是素數了,因為在區間 [sqrt(n),n] 也一定不會發現可整除因子。

現在,isprime 函式的時間複雜度降為 o(sqrt(n)),但是我們實現 countprimes 函式其實並不需要這個函式,以上只是希望讀者明白 sqrt(n) 的含義,因為等會還會用到。

高效實現 countprimes

高效解決這個問題的核心思路是和上面的常規思路反著來:

首先從 2 開始,我們知道 2 是乙個素數,那麼 2 × 2 = 4, 3 × 2 = 6, 4 × 2 = 8… 都不可能是素數了。

然後我們發現 3 也是素數,那麼 3 × 2 = 6, 3 × 3 = 9, 3 × 4 = 12… 也都不可能是素數了。

看到這裡,你是否有點明白這個排除法的邏輯了呢?先看我們的第一版**:

int

countprimes

(int n)

如果上面這段**你能夠理解,那麼你已經掌握了整體思路,但是還有兩個細微的地方可以優化。

首先,回想剛才判斷乙個數是否是素數的 isprime 函式,由於因子的對稱性,其中的 for 迴圈只需要遍歷[2,sqrt(n)]就夠了。這裡也是類似的,我們外層的 for 迴圈也只需要遍歷到 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 的整數倍都標記為 false,但是仍然存在計算冗餘。

比如 n = 25,i = 4 時演算法會標記 4 × 2 = 8,4 × 3 = 12 等等數字,但是這兩個數字已經被 i = 2 和 i = 3 的 2 × 4 和 3 × 4 標記了。

我們可以稍微優化一下,讓 j 從 i 的平方開始遍歷,而不是從 2 * i 開始:

for

(int j = i * i; j < n; j +

= i)

isprim[j]

=false

;

這樣,素數計數的演算法就高效實現了,其實這個演算法有乙個名字,叫做 sieve of eratosthenes。看下完整的最終**:

int

countprimes

(int n)

該演算法的時間複雜度比較難算,顯然時間跟這兩個巢狀的 for 迴圈有關,其運算元應該是:

n/2 + n/3 + n/5 + n/7 + … = n × (1/2 + 1/3 + 1/5 + 1/7…)

括號中是素數的倒數。其最終結果是 o(n

∗log

logn

)o(n * loglogn)

o(n∗lo

glog

n),有興趣的讀者可以查一下該演算法的時間複雜度證明。

你真的懂素數嗎?

素數 即質數 定義 大於1的自然數中,除了1和此整數自身外,沒法被其他自然數整除的數 即除了1和本身沒有其他因子 首先,我們先來看乙個數與其因子有什麼關係 除了本身的因子 因子 數本身 2 如10 的因子 1 2 5 因為2 5 10 5是10的1 2 如果a因子大於5,則b因子就必須是 1,2 即...

你真的會暴力嗎

problem 你真的會暴力嗎 description 給你乙個長度為n的數列,問你這個數列是不是乙個完美數列。乙個數列是完美數列當且僅當不存在兩個不相交的子串行,元素的數值和相同。input 第一行乙個整數t t 50 表示一共有t組。每組第一行乙個整數n n 1e4 表示數列的長度為n,接下來一...

if else 你真的會嗎?

目錄 為什麼我們寫的 都是if else?if else 太多有什麼缺點?是否有好的方法優化?如何重構?異常邏輯處理型重構方法例項一 異常邏輯處理型重構方法例項二 異常邏輯處理型重構方法例項三 異常邏輯處理型重構方法例項四 狀態處理型重構方法例項一 狀態處理型重構方法例項二 總結 盡可能地維持正常流...