线性筛素数
问题:给出n,输出所有<=n的素数。
1.暴力做法:
枚举一个 i (3<=i<=n) 暴力判断 i 是否是素数,是的话输出。
复杂度O(nlog(n)),海星。但是筛素数往往只是解题的一小步,这个复杂度有点高了。
2.Eratosthenes筛法:
第一种方法由于要挨个判断,复杂度高了些。为了避免不必要的判断,我们可以在找出一个素数时,筛掉它所有的倍数。这样复杂度降到了O(nloglog(n)),非常接近线性。
(lyd:卡这种常数的简直是丧病)
代码实现也很简单:
#include<iostream>
#include<cstdio>
#define maxn 1000005
using namespace std;
int v[maxn],pri[maxn];
int m=0,n;
void primes(int n)
{
for(int i=2;i<=n;i++){
if(v[i])continue;
printf("%d ",i);
for(int j=1;j<=n/i;j++){
v[i*j]=1;
}
}
}
int main()
{
scanf("%d",&n);
primes(n);
}
3.线性筛素数
虽然上一种筛法已经比较优秀了,但还不是线性。
现在如果要重新开辟一条思路,比较难。所以我们考虑,在筛法2中做了哪些无用功。
合数会被它的每一个质因子筛。
如果线性筛法,应该找一个办法,使得每一个合数只被筛一次,这样就保证了线性的复杂度。
最经典的做法就是让每一个合数只被它最小的质因子筛掉。
一个例子:n=10;
代码(一会会跑一遍):
#include<iostream>
#include<cstdio>
#define maxn 1000005
using namespace std;
int v[maxn],pri[maxn];
int m=0,n;
void primes(int n)
{
for(int i=2;i<=n;i++){
if(!v[i]){
v[i]=i;
pri[++m]=i;
}
for(int j=1;j<=m;j++){
if(pri[j]>v[i]||pri[j]>n/i)break;
v[i*pri[j]]=pri[j];
}
}
}
int main()
{
scanf("%d",&n);
primes(n);
for(int i=1;i<=m;i++)printf("%d ",pri[i]);
}
模拟过程(水印遮掉的部分都是2,3,5,7):
好的,线性筛就完了。
欧拉函数
问题:给定n,问1~n中与n互质的数的个数。
欧拉函数常用 表示。
按照上方筛法2,p会筛掉 n/p 个数,q会筛掉 n/q 个数,
根据容斥原理,最后剩下的个数应该是
依次类推,证毕。
根据以上,可以通过分解质因数来求欧拉函数。
但很多时候都要求1~n的欧拉函数,这就需要线性推出欧拉函数。
首先要明确一些东西:
欧拉函数是积性函数,即:
于是就有了一些引理:
1.若 且
,
2.若 且
不整除 n ,
证明:
1.把 n 和 ( n / p ) 分解质因数,公式连乘部分相同,只是'n'的部分多了p,所以成立。
2.此时 n 和 ( n / p ) 互质,根据积性函数可得引理2。
有了这两个引理,就可以在线性筛的时候顺便筛欧拉函数了。
#include<iostream>
#include<cstdio>
#define maxn 1000005
using namespace std;
int v[maxn],pri[maxn],phi[maxn];
int m=0,n;
void primes(int n)
{
for(int i=2;i<=n;i++){
if(!v[i]){
v[i]=i;
pri[++m]=i;
phi[i]=i-1;
}
for(int j=1;j<=m;j++){
if(pri[j]>v[i]||pri[j]>n/i)break;
v[i*pri[j]]=pri[j];
phi[i*pri[j]]=phi[i]*(i%pri[j]?pri[j]-1:pri[j]);
}
}
}
int main()
{
scanf("%d",&n);
primes(n);
for(int i=1;i<=m;i++)printf("%d ",pri[i]);
cout<<endl;
for(int j=1;j<=n;j++)printf("%d ",phi[j]);
}
我数学怎么这么菜啊