欧拉筛法:
直接判断素数耗时,换个角度标记非素数,那么没有被标记的就是素数。
关键点:
- 除2之外的偶数必然不是素数,所以只需要遍历奇数
- 寻找0-N范围中的素数时,遍历只需要从 2-->sqrt(N+0.5),因为sqrt(N+0.5)之后的数必然在第二层的循环中标记过。
- 二层循环的意思:一层循环找到了素数prime,则要将prime的所有倍数都筛去。注意从prime*prime开始筛,因<prime*prime的数必然能被之后找到的prime的倍数筛去。
const int maxn = 10004;
bool vis[maxn];
int prime[maxn], cnt;
void sort_prime() {
prime[cnt++] = 2;
int m = (int) sqrt(maxn + 0.5);
for(int i=3; i<m; i+=2) {
if(!vis[i]) {
prime[cnt++] = i;
for(int j=i*i; j<maxn; j+=i) {
vis[j] = true;
}
}
}
return;
}
欧拉函数:
求解所有<=n且与n互素的数的总数,特别的 phi(1) = 1。 (phi其实是数学符号的谐音~)
根据性质 phi(n) = n*(1-1/p1)*(1-1p2)...*(1-1/pk),pi是n的所有素因子。性质的公式进行变行--> phi(n) = n ∏ [ (pi-1)/pi ] --> 每一步看phi = n,phi = phi / pi * (pi-1)先除后乘防止溢出-->phi = phi - phi/pi -- > phi -= phi/pi。
int phi(int N) {
int tmp = N, n = N;
for(int i=0; i<cnt && prime[i]<=n; ++i) {
if(n%prime[i] == 0) {
tmp -= tmp/prime[i]; //欧拉函数性质推导出的式子
while(n%prime[i] == 0) // 去掉prime[i]因子
n /= prime[i];
}
}
//若n != 1,则必然还剩下一个素数没有用
if(n != 1) tmp -= tmp/n;
return tmp;
}