素數的求解方法

2021-08-03 07:46:37 字數 3898 閱讀 9799

就判斷素數而言,事實上是非常簡單的了。根據定義,判斷乙個整數n是否是素數,只需要去判斷在整數區間[2, n-1]之內,是否具有某個數m,使得n % m == 0。**可以這麼寫:

int isprime(int n)   

return

1;

}

事實上,這個演算法是o(n)的,感覺是很快了,但是依舊無法滿足需求。所以有乙個演算法是o(sqrt(n))的演算法。**可以這麼寫:

int isprime(long

long n)

return

1;

}

原理很巧妙,也僅僅是把**的i < n變成了i * i <= n而已,但是優化卻是極其高的。可能你會注意到,在上乙份**裡面,我定義的n為int型別,而後面乙份**,我卻定義成了long long,這是因為在1s內,上乙份**能判斷出來的數量級為1e8,而後面乙份**判斷出來的數量級卻幾乎可以達到1e16。而原因僅僅是因為a * b = c的話一旦a是c的約數,那麼b也是,如果a不是,那麼b也不是。

這個方法也可以稱作試除法。

儘管上面的o(sqrt(n))的演算法已經非常優秀了,但是面對更高數量級的「大數」卻會顯得力不從心。而這個時候,miller_rabin的優越性就顯示出來了。

miller_rabin的理論基礎**於費馬小定理。值得一提的是費馬小定理是數論四大定理之一。

費馬小定理:n是乙個奇素數,a是任何整數(1≤ a≤n-1) ,則 a^(n-1)≡1(mod n)

要測試n是否是乙個素數,首先將n-1分解為(2^s) * d,在每次測試開始時,先隨機選擇乙個介於[1, n - 1]的整數a,之後如果對於所有的r∈[0, s - 1],若a^d mod n ≠ 1且a^((2^r) * d) mod n ≠ -1,那麼n就是乙個合數,否則n有3/4的機率是素數。

這也是為什麼說這個演算法只是素性測試了,他不能完全保證結果正確,但是當測試次數大約為20的時候,基本可以確保正確率達到97%以上。

它的**可以這麼寫:

const int s = 20;  

ll mod_mul(ll a, ll b, ll n)

return res;

} ll mod_exp(ll a, ll b, ll n)

return res;

} bool miller_rabin(ll n)

srand((ll)time(null));

for (int i = 0; i < s; ++i)

if (x != 1) return

false;

} return

true;

}

上面介紹的一些素數判斷的演算法,在某些問題是基本可以適用的了。但是對於另外一類問題卻十分尷尬。比如問你整數區間[1, n]內的素數個數是多少。這個時候如果乙個個列舉,乙個個判斷,對於樸素方法來說,最優也是o(nsqrt(n)),即使是miller_rabin演算法也無法在o(n)的時間內得到結果。於是就有了埃氏篩選法(埃拉託斯特尼篩法)。

對於篩選整數n以內的素數,演算法是這麼描述的:先把素數2的倍數全部刪除,剩下的數第乙個為3,再把素數3的倍數全部刪除,剩下的第乙個數為5,再把素數5的倍數全部刪除······直到把n以內最後乙個素數的倍數刪除完畢,得到n以內的所有素數。
**可以這麼寫:

const

int maxn = 1e7 + 5;

int pri[maxn];

void getprime(int n)

} }

而事實上,觀察可以發現,上面的這種寫法有很多次重複計算,這樣顯然無法做到線性篩選,而另外一種寫法卻可以得到線性篩選,達到時間複雜度為o(n),**可以這麼寫:

const

int maxn = 1e7 + 5;

void getprime()

} }

來自kuangbin的模板。

從上面的**可以發現,顯然這種篩法只能應付達到1e7這種數量級的運算,即使是線性的篩選法,也無法滿足,因為在acm競賽中,1e8的記憶體是極有可能獲得memery limit exceed的。

於是可以考慮容斥原理。

以ahuoj 557為例,1e8的情況是篩選法完全無法滿足的,但是還是考慮a * b = c的情況,1e8只需要考慮10000以內的素數p[10000],然後每次先減去n / p[i],再加上n / (p[i] * p[j])再減去n / (p[i] * p[j] * p[k])以此類推…於是就可以得到正確結果了。

**如下:

#include 

#include

using

namespace

std;

const

int maxn = 10005;

int sqrn, n, ans = 0;

bool vis[maxn];

int pri[1500] = ;

void init()

} } void dfs(int num, int res, int index)

dfs(num + 1, res * pri[i], i+1);

if(num % 2 == 1)else

if(num == 1) ans++;

} } int main()

return

0;

}

最後介紹的這個演算法可以說是黑科技級別的,它能夠在3s內求出1e11之內的素數個數。據說還有在300ms內求出1e11的個數的。可以參考wiki裡面原理。然後給出來自codeforces 665f題目裡面的**。

#define maxn 100    // pre-calc max n for phi(m, n)  

#define maxm 10010 // pre-calc max m for phi(m, n)

#define maxp 40000 // max primes counter

#define max 400010 // max prime

#define setbit(ar, i) (((ar[(i) >> 6]) |= (1 << (((i) >> 1) & 31))))

#define chkbit(ar, i) (((ar[(i) >> 6]) & (1 << (((i) >> 1) & 31))))

#define isprime(x) (( (x) && ((x)&1) && (!chkbit(ar, (x)))) || ((x) == 2))

namespace pcf ;

int len = 0, primes[maxp], counter[max];

void sieve()

} for (int i = 1; i < max; i++)

} void init()

} }

long

long phi(long

long m, int n)

long

long lehmer(long

long m)

}

素數的若干求解方法

最直觀的方法,根據定義,因為質數除了1和本身之外沒有其他約數,所以判斷n是否為質數,根據定義直接判斷從2到n 1是否存在n的約數即可。c 如下 cpp view plain copy bool isprime 1 intnum 上述判斷方法,明顯存在效率極低的問題。對於每個數n,其實並不需要從2判斷...

素數求解的的幾種簡單方法

問題 列印出100到200之間的素數 方法一 素數n就是除了1和它本身之外沒有任何因子的數,所以要求素數我們很容易想到從2到n 1去試除,如果能除盡說明它不是素數,這個時候就接著判斷下乙個數也就是下面的這種 上結果圖 方法二 方法一是可以做出來,但是要試的因子太多了有點。我們可以想到如果n有除了它本...

關於素數的求解

質數 prime number 又稱素數,有無限個。乙個大於1的自然數,除了1和它本身外,不能被其他自然數整除,換句話說就是該數除了1和它本身以外不再有其他的 因數。求解整數n之前的所有素數及總數 include stdio.h include math.h int main if c sqrt i...