算法——素數問題全解
1.線性素數篩(試除法)
適用範圍:判定數據不大,數據量級別較小(一般超過10000就要慎重了)。
一般來說,“素數判定” 對這道題目來說只是輔助的話,用這個方法就可以過的。
int Isprime(int x) { //素數定義為大於1的自然數,不用考慮出現0,1負數的情況 for (int i = 2; i <= sqrt(x); i++) { if (x%i == 0) return 1; } return 0; }
題目:HDU 1262
#include <iostream> #include <math.h> usingView Codenamespace std; int Isprime(int x) { //素數定義為大於1的自然數,不用考慮出現0,1負數的情況 for (int i = 2; i <= sqrt(x); i++) { if (x%i == 0) return 1; } return 0; } int main() { int x, y; while (cin >> x >> y) { if (x == 0 && y == 0)break; int flag = 1;for (int i = x; i <= y; i++) { int n = pow(i, 2) + i + 41; if (Isprime(n)) { flag = 0; break; } } if (flag) cout << "OK" << endl; else cout << "Sorry" << endl; } }
題目:HDU 2012
這題有點意思,一個偶數 N 可以分成兩個距離 N/2 距離一樣的素數的和。比如:20=7+13
#include <iostream> using namespace std; int Isprime(int x) { int i; for (i = 2; i <= sqrt(x); i++) { if (x%i == 0) { return 1; } } return 0; } int main() { int n; while (cin >> n) { int l, r; l = r = n / 2; while (Isprime(l) || Isprime(r))l--, r++; cout << l << " " << r << endl; } return 0; }View Code
2.埃式/歐拉篩(打表法)
適用範圍:數據量級別巨大。例如:N<10^6,問N是第幾個素數、輸出第一百萬個素數(這個更麻煩一些)
- 埃式篩
基本思想:質數的倍數一定不是質數。
int M[1000000]; void AiSieve() { int count = 0; for (int i = 2; i < 1000000; i++) { if (M[i] == 0) { M[i] = ++count; for (int j = 2 * i; j < 1000000; j += i) { M[j] = -1; } } } }
- 歐拉篩
基本思想 :在埃氏篩法的基礎上,讓每個合數只被它的最小質因子篩選一次,以達到不重復的目的。
int M[1000001]; int prime[80000]; void EluerSieve() { int count = 0; for (int i = 2; i <= 1000000; i++){ if (!M[i]){ prime[count++] = i; } for (int j = 0; j < count && prime[j] * i <= 1000000; j++){ M[i*prime[j]] = -1; if (i%prime[j] == 0) break; } } }
題目:HDU 2136
這題輸出的數據級別太大了,用 cout 是過不了的,坑B, 。
#include <iostream> using namespace std; int M[1000000]; int main() { int count = 0; for (int i = 2; i < 1000000; i++) { if (M[i] == 0) { M[i] = ++count; for (int j = 2 * i; j < 1000000; j+=i) { M[j] = count; } } } int n; while (scanf("%d", &n) != EOF) { //cout << M[n] << endl; printf("%d\n",M[n]); } }View Code
3.Meisell-Lehmer算法(求2...n範圍內的素數個數)
這裏為什麽是空的呢,因為我還沒看懂。
4.看臉OJ法(Miller - Rabin素數測試)
適用範圍:數值特別大,(C++ 受long long數據範圍限制,穩妥點只能到10^10)
原理:費馬小定理_百度百科,簡單來說:假如p是質數,且gcd(a,p)=1,那麽 a(p-1)≡1(mod p)。反過來不一定成立。即當a^(p?1)%p=1ap?1%p=1時,p未必是質數。但是這個概率比較小。所以,做的次數越多,正確率越高,只要出現一次不為1,就一定不是素數。
要做兩點:
1. 打個素數表,方便取 a 的值(但要註意,a不能和p值取相同)。
2. 快速冪,問題就在這,我們要判斷的數,在快速冪裏是mod數,而這個mod通常很大,那麽這句話 a = ((a%mod)*(a%mod)) % mod; 取余的意義其實不大,long long 十進制下19位,mod達到10位,假如正巧不巧,a正好就比mod小一點點,就很可能爆範圍了(但是,我們做上百次檢測,這個可能性就很大了)。
所以這個就很難受,判斷大整數,還做不了太大的,就算能自己實現一個大整數,運行速度上也達不到要求了。
#include <iostream> #include <math.h> #include <time.h> using namespace std; int M[100001]; int prime[8000]; void EluerSieve() { int count = 0; for (int i = 2; i <= 100000; i++){ if (!M[i]){ prime[count++] = i; } for (int j = 0; j < count && prime[j] * i <= 100000; j++){ M[i*prime[j]] = -1; if (i%prime[j] == 0) break; } } } int fastpower(int down,int up,int mod) { long long a=down, res = 1; while (up) { if (up & 1) res = ((res%mod)*(a%mod)) % mod; a = ((a%mod)*(a%mod)) % mod; up >>= 1; } return res; } int MillerRabin(int x) { srand(time(0)); for (int i = 0; i < 1000; i++) { int a = prime[rand()%10000]; if (a == x) { i--; continue; } if (fastpower(a, x - 1, x) != 1) { return 0; } } return 1; } int main() { int N; EluerSieve(); while (cin >> N) { //cout << M[N] << endl; cout << MillerRabin(N) << endl; } }
算法——素數問題全解