-
素數判定
所謂素數: 指恰好有2個約數的整數。
判定: 因為n的約數都不超過n, 所以只要檢查 2 ~ n-1 的所有整數是否整除n就能判定n是不是素數。
在此,如果d 是 n的約數, 那么 n/d也是n的約數。由n = d * n / d 可知 min(d, n/d) <= 根號n , 所以只要檢查 2 ~ 根號n 的所有整數就足夠了。
同理可知,整數分解和約數枚舉都可以在 O(根號n) 時間完成。(還有更高效的算法)
#include <iostream> #include <map> #include <vector> using namespace std; //假設輸入的都是正數 //素性測試 bool is_prime(int n) { for (int i = 2; i * i <= n; i++) { if (n % i == 0) return false; } return n != 1; //1是例外 } //約數枚舉 vector<int> divisor(int n) { vector<int> res; for (int i = 1; i * i <= n; i++) { if (n % i == 0) { res.push_back(i); if (i != n / i) { res.push_back(n / i); } } } return res; } //整數分解 map<int, int> prime_factor(int n) { map<int, int> res; for (int i = 2; i * i <= n; i++) { while (n % i == 0) { ++res[i]; n /= i; } } if (n != 1) { res[n] = 1; } return res; } int main() { //記住這個就差不多了 cout << "素性測試: " << is_prime(7) << endl; cout << "==================================================\n"; cout << "約數枚舉(約數總數為2,則為素數):\n"; vector<int> res = divisor(13); // for (const auto& e : res) { // cout << e << " "; // } cout << (res.size() == 2) << endl; cout << "==================================================\n"; cout << "整數分解(他沒有被任何數分解:\n"; int n = 19; map<int, int> res2 = prime_factor(n); // for (auto it = res2.begin(); it != res2.end(); ++it) // { // cout << it->first << " " << it->second << " "; // cout << endl; // } cout << (res2[n] == 1) << endl; return 0; }
-
誒氏篩法
如果只對一個整數進行素性測試, 通常 的算法就夠了,如果要對許多整數進行素性測試,則有更高效的算法。
素數的個數
給定整數n, 請問n以內有多少個素數?
限制條件:
n <= 10^6
#include <iostream> using namespace std; const int maxn = 10000000; int prime[maxn]; //第i個素數 bool is_prime[maxn + 1]; //is_prime[i]為true 表示i是素數 //返回n以內的素數個數 int sieve(int n) { int p = 0; //統計素數的個數 //初始化 for (int i = 0; i <= n; i++) { is_prime[i] = true; } is_prime[0] = is_prime[1] = false; for (int i = 0; i <= n; i++) { if (is_prime[i]) { prime[p++] = i; //將素數添加到prime中 //1.首先2是素數, 然后划去所有2的倍數 //2.表中剩余的最小數字是3, 它不能被更小的數整除, 所以是素數。再將表中所有3的倍數都划去 //3.依次類推, 如果表中剩余的最小數字是m時, m就是素數。然后將表中所有m的倍數都划去。像這樣,就能依次枚舉n以內的素數。 for (int j = 2 * i; j <= n; j += i) { is_prime[j] = false; } } } return p; } int main() { int cnt = sieve(30); for (int i = 0; i < cnt; i++) { cout << prime[i] << " "; } cout << endl; return 0; }
-
用埃氏素數解決藍橋杯簡單題
第八屆藍橋杯第二題:
標題:等差素數列
2,3,5,7,11,13,....是素數序列。
類似:7,37,67,97,127,157 這樣完全由素數組成的等差數列,叫等差素數數列。
上邊的數列公差為30,長度為6。
2004年,格林與華人陶哲軒合作證明了:存在任意長度的素數等差數列。
這是數論領域一項驚人的成果!
有這一理論為基礎,請你借助手中的計算機,滿懷信心地搜索:
長度為10的等差素數列,其公差最小值是多少?
注意:需要提交的是一個整數,不要填寫任何多余的內容和說明文字。
#include <iostream> using namespace std; const int maxn = 10000000; int prime[maxn]; //第i個素數 bool is_prime[maxn + 1]; //is_prime[i]為true 表示i是素數 int sieve(int n); bool isPrime(int n); void solve(); //返回n以內的素數個數 int sieve(int n) { int p = 0; //統計素數的個數 //初始化 for (int i = 0; i <= n; i++) { is_prime[i] = true; } is_prime[0] = is_prime[1] = false; for (int i = 0; i <= n; i++) { if (is_prime[i]) { prime[p++] = i; //將素數添加到prime中 //1.首先2是素數, 然后划去所有2的倍數 //2.表中剩余的最小數字是3, 它不能被更小的數整除, 所以是素數。再將表中所有3的倍數都划去 //3.依次類推, 如果表中剩余的最小數字是m時, m就是素數。然后將表中所有m的倍數都划去。像這樣,就能依次枚舉n以內的素數。 for (int j = 2 * i; j <= n; j += i) { is_prime[j] = false; } } } return p; } //素性測試 bool isPrime(int n) { for (int i = 2; i * i <= n; i++) { if (n % i == 0) return false; } return n != 1; //1是例外 } void solve() { int N = 10000; int cnt = sieve(N); // cout << cnt << endl; for (int i = 1; i <= N; i++) //枚舉 1~1000的公差 { for (int j = 0; j < cnt; j++) { int flag = 1, //標志 tmp = prime[j]; //第j個素數 for (int k = 1; k < 10; k++) //第一個數已經確定是素數 { if (tmp + i > N || !isPrime(tmp + i)) { flag = 0; break; } else { tmp = tmp + i; //下一個數 } } //如果 連續9個公差為i的數都是素數 if (flag) { cout << i << " 開始的素數:" << prime[j] << endl; return; } } } } int main() { solve(); return 0; }
-
練習題
POJ 3126: Prime Path
POJ 3421: X-factor Chains
POJ 3292: Semi-prime HB-numbers
