歐拉篩
質數篩
也稱線性篩
它比時間復雜度為 \(O(n\log\log n)\) 的埃氏篩更優,因為埃氏篩會有篩重。
歐拉篩保證每個合數只會被它的最小質因數篩掉,所以每個數只會被篩一次。
時間復雜度 \(O(n)\)
#include <vector>
#include <bitset>
std::vector<int> prime;//存放篩出來的質數
std::bitset<100000003> cmpst;//是否是合數(composite)
inline void Euler(register int n) {
cmpst.reset();
for (register int i=2; i<=n; ++i) {
if (!cmpst[i]) prime.push_back(i);//不是合數
for (register int p : prime) {
if (i * p > n) break;
cmpst[i*p] = 1;//標記合數
if (i % p == 0) break;//核心操作:此時p是i的最小質因數
}
}
}
歐拉函數篩
特殊地,對於一個質數 \(p\) ,有 \(\varphi(p)=p-1\)
同時,因為歐拉函數 \(\varphi\) 是積性的,所以有 \(\forall\gcd(a,b)=1,\ \varphi(a\times b)=\varphi(a)\times\varphi(b)\)
又因為對於任意兩個質數 \(p_1,p_2\) ,必有 \(\gcd(p_1,p_2)=1\) ,所以我們考慮用質數篩出合數的 \(\varphi\) 值。
時間復雜度 \(O(n)\)
#include <vector>
#include <bitset>
template<int N> class Phi {
private:
int n, phi[N];
std::vector<int> prime;
std::bitset<N> cmpst;//composite;
void Euler() {
cmpst.reset();
phi[1] = 1;
for (register int i=2, tmp; i<=n; ++i) {
if (!cmpst[i]) {
prime.push_back(i);
phi[i] = i-1;//質數的phi值
}
for (register int p : prime) {
tmp = i*p;
if (tmp > n) break;
cmpst[tmp] = 1;//標記合數
if (i%p == 0) {//同樣的核心操作
phi[tmp] = phi[i] * p;
break;
} phi[tmp] = phi[i] * phi[p];//合數的phi值,利用phi的積性
}
}
}
public:
Phi() : n(N<1?0:N-1) { Euler(); }
Phi(int _n) : n(_n) { Euler(); }
void build(int _n) { n = _n, Euler(); }
int operator () (int x) const { return phi[x]; }
int max_n() const { return n; }
};
