)了解了基本的判斷方法後,你是不是有個疑問:「我們能判斷素數的個數嗎?」總所周知,素數的個數是無限的,且沒有固定的公式…但如果我們只要判斷[a, b]區間(a, b範圍為1到1億)內的素數的個數呢?
首先,我們可以想到,如果要求的素數個數區間[a, b],當區間長度比較小(10^6內),我們可以用篩法求出區間內的所有的素數,然後統計個數即可。
但如果區間長度很長或者要求詢問的次數很多,那該怎麼辦呢? [a,b]區間內素數的個數 = [1, b]的個數 - [1, a - 1]的個數,所以我們這裡只討論求[1, a]區間內的素數。以下提供個人的兩種方法,時限都是1s內產生結果。如果哪位大牛有更好的方法,大家一起交流下~~
1. 我們可以擴充套件上面的思想,當區間小的時候,我們可以很好的求出素數的個數。那我們可以把大的區間劃分成一塊塊小的區間,比如把乙個長度為1億的區間劃分1,000個長度為100,000的區間。我們可以利用miller-rabin事先把[1, 100000], [100001, 200000], [200001, 300000]的區間內的素數個數統計好,然後存在乙個陣列中。
完成這步後,思路就比較清晰:對於區間[1, a],可以拆分為乙個個長度為100000的小區間([1, 100000], [100001, 200000]…),加上尾部的小區間[c * 100000, a]。前面的小區間只要陣列的值相加即可,而後面的小區間[c * 100000, a],長度在100000內,直接用區間的篩法求出素數,統計個數即可。
該方法速度很快,主要時間都花在陣列打表上,然後直接存在陣列裡,求1到1億的素數個數時間為0.06s。
評價:優點是方法速度快,且直接套模板即可。缺點是需要事先打表,且**長度很長(因為要給長度為1000的陣列賦初值)。
2. 第二種方法涉及到容斥原理(inclusion-exclusion principle),容斥原理參見(
)。當乙個數是合數,那麼它可以分解成幾個素數的乘積。如30 = 2 * 3 * 5。我們可以統計合數的個數,然後拿總數減它就是素數的個數(注意還要去掉1的)。我們可以利用類似篩法的原理,去除2的倍數(它們肯定是合數,不包括2),然後去除3的倍數,5的倍數,知道去除到sqrt(a)的倍數為止。但你會發現6 = 2 * 3,被去除了2次,而這正是容斥原理解決的問題。合數的個數 = 1個素數篩完的合數個數 – 2個素數篩完的合數個數 + 3個素數篩完的合數個數...
而容斥原理的累加過程,即可用dfs來解決。你可能會認為sqrt(1億) = 10000,其中素數有很多,dfs要跑很長時間。但我們只需要加一些簡單的優化即可很大程度地提高程式的效率。
首先,我們寫篩法出1到sqrt(a)的素數表,然後從小到大dfs。
如果當前的乘積 > a,那麼直接退到上一層。
如果該層的所有乘積不能使總數發生變化(即所有乘積都 > a),那麼直接退回第一層。(因為是從小到大,該層下面的乘積必將 > a)
如果是第一層的所有乘積不能使總數發生變化,那麼程式執行結束。(原理同上)
經過這樣優化後,求1到1億的時間為0.4秒,1到10億的時間為3.5s。
核心**:
void solve(int index, int lcm, int k)
for (i = index; i < total - k + 1; i++)
if (t_temp == temp) return ; // 剪枝:同樣道理,說明以後的k - 1個不能組成我們想要的值}}
main()中:
for (k = 1; k <= total; k++)
評價:該方法巧妙地使用了容斥原理來計數,且dfs應用于容斥原理的剪枝十分重要。
提到容斥原理,推薦一道前不久做的題目srm 453.5 div 1 1000(
),兩題的容斥原理思想差不多,但剪枝方法不同,而且兩題的方法交換都會產生超時...(有興趣一起交流下~~)
1億以內素數的個數 數論基礎素數篩
一.什麼是素數 長話短說 因數只有1和本身的正整數,但素數不包含1 比如 2 3,5,7.二.如何判斷乙個數是素數 用1中的定理,也就是說,如果某個大於1的正整數與任何小於等於它的正整數的最大公因數都是1,則為素數。咳咳咳.扯遠了,我們的重心不在這裡,重點在下面 三.什麼是素數篩 首先,給你乙個區間...
Python基礎練習之素數
素數 質數 範圍100 200 素數判斷除了自身i和1其他任何數都不能被整除 m for i in range 100,201 n print i for j in range 2,20 print j print i,j,i j print len n if min n 0 and len n 1...
數論基礎之素數,約數
關於約數 唯一分解定理 任何數都可由素因子之積構成 int p 100 a 100 cnt void divide int n if n 1 for int i 1 i cnt i cout 方法2 離線做法,預處理出乙個質因子樹。預處理o nlogn 查詢乙個數o logn void init v...