欧拉筛

欧拉筛

质数筛

也称线性筛

它比时间复杂度为 \(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; }
};
posted @ 2021-11-13 21:35  Gyan083  阅读(695)  评论(0编辑  收藏  举报