素數
阿新 • • 發佈:2020-07-11
雜
學數論 心情複雜 沒什麼好說的 , , ,, z h 大佬!!
素數
定義
就是質數 , ( 1不是素數) , 一個比 1 大的整數若沒有約數(1不算) , 稱為合數
整除
若 \(a=kd\) ( \(k\) 為整數) , 則稱 \(d\) 整除 \(a\) , 記做 \(d\)
素數計數函式
小於/等於 \(x\) 的素數的個數 , 用 \(\pi(x)\) 表示 , 隨著 \(x\) 的增大 , 有這樣的近似結果 :
\[\pi(x) \sim \frac{x}{ln(x)} \]
素數篩法
暴力 \(O(n\sqrt n)\)
對於檢驗一個數是不是素數 , 暴力做法 --- 列舉 ,, 穩妥 , 但沒必要 , 顯然 , 若 \(x\)
bool isPrime(a) {
if (a < 2) return 0; // 1是約數
for (int i = 2; i * i <= a; i++)
if (a % i == 0) return 0;
return 1;
}
\(\mathfrak{Eratosthenes}\) 篩法 (埃拉託斯特尼篩法 / 埃氏篩) \(O(n~log~log~n)\)
考慮到對於合數 \(x\) , \(x\) 的倍數一定也是合數 , 從小到大篩 , 順手把當前數的倍數標記為合數 , 最後沒被標記 (tag[i] = 1
) 的數為素數 , 並且都存到了 \(prime\) 數組裡
void Eratosthenes(int n) { int p = 0; for (int i = 0; i <= n; i++) tag[i] = 1; tag[0] = tag[1] = 0; //以上為初始化標記 for (int i = 2; i <= n; i++) { if (tag[i]) { prime[p++] = i; //p為當前素數數量 for (int j = i * i; j <= n; j += i) tag[j] = 0; //已經篩過了2到i-1的倍數,所以直接從i開始 } }//需要返回質數個數的話寫成int的函式 return p 即可 }
\(\mathfrak{Euler}\) 篩法 (尤拉篩 / 線性篩) \(O(n)\)
考慮到埃氏篩把一些合數重複篩到 , 所以還不夠優 , 尤拉篩原理 : 用 最小質因子 篩
int pri[n], vis[n];
void init() {
phi[1] = 1;
int cnt = 0;
for(int i = 2; i <= n; i++) {
if (!vis[i]) {
phi[i] = i - 1;
pri[cnt++] = i;
}
for (int j = 0; j < cnt; j++) {
if (1ll * i * pri[j] >= n) break;
vis[i * pri[j]] = 1;
if (i % pri[j]) {
phi[i * pri[j]] = phi[i] * (pri[j] - 1);
} else {
// i % pri[j] == 0
// 換言之,i 之前被 pri[j] 篩過了
// 由於 pri 裡面質數是從小到大的,所以 i 乘上其他的質數的結果一定也是
// pri[j] 的倍數 它們都被篩過了,就不需要再篩了,所以這裡直接 break
// 掉就好了
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
}
}
}
未完..