我筛筛筛筛筛!
小学生筛
#include<iostream>
using namespace std;
int main()
{
int n;
cin>>n;
//枚举的次数不会超过i^2,节省时间复杂度
for(int i=2;i<=n;i++)//素数从2开始数
{
while(n%i==0)
{
cout<<i<<" ";
n=n/i;
}
}
if(n>1)
{
cout<<n;
return 0;
}
}
素不素很简单捏?
但如果把此代码的数据范围加到1e5左右,就会发现TLE了。
这只能说明一件事,那就是 这种算法low爆了 还可以优化代码,最大限度减少循环的执行次数,或者直接删掉不必要的循环。
埃氏筛
埃拉托斯特尼筛法,简称埃氏筛或爱氏筛,是一种由希腊数学家埃拉托斯特尼所提出的一种简单检定素数的算法。要得到自然数n以内的全部素数,必须把不大于根号n的所有素数的倍数剔除,剩下的就是素数。
例:查找 2 3 4 5 6 7 8 9 10 11中的素数
s
t
e
p
1
step1
step1: 2是素数,2的倍数包括2 4 6 8 10
删掉2的倍数得到新数组 2 3 5 7 9 11
s
t
e
p
2
step2
step2: 3是素数,3的倍数包括3 6 9
删掉3的倍数得到新数组2 3 5 7 11
s
t
e
p
3
step3
step3: 5是素数,5的倍数包括5 10
删掉5的倍数得到新数组 2 3 5 7 11
代码实现
const int N = 1e7 + 5;
int st[N];//标记数组
void E_sieve(int n){
for(int i = 2; i <= n; i++)
{
if(st[i] == 0)//如果i是素数
{
for(int j = 2 * i; j <= n; j += i)//从i的2倍开始删
st[j] = 1; //标记删除
}
}
}
int main()
{
int n;
cin>>n;
E_sieve(n);
}
埃氏筛的时间复杂度在O(nlog(nlog(n)))左右,已经接近O(n)。
相对于接下来的欧拉筛,虽然慢一点,但是一个更好理解且相对好用的算法。
欧拉筛
欧拉筛是真正的线性筛法,它的时间复杂度=O(N)。
可以用st表实现,但是会慢一些,所以不建议。
同样沿用前一部分的数组样例:在数组2 3 4 5 6 7 8 9 10 11 中,
2的倍数包括:2 4 6 8 10
3的倍数包括:3 6 9
5的倍数包括:5 10
可以发现,6 10被重复删去了多次。这增加了不必要的查找次数。
那怎么删呢?
依旧用上面的例子,10被25和52同时删掉两次,为了确保只让最小质因子去删,我们需要让最小的质因子最大的倍数,所以要到最后一次再删。
例2 3 4 5 6 7 8 9 10 11 12
第一次 2存入primes数组,删掉22(4),2/2=1,break;
第二次 3存入primes数组,删掉32 33(6,9),3/3=1,break;
第三次 按照步长为1,删掉42(8);重要的来了:4 * 3 = 12,删吗?不删!
因为34是一定能够被2*6筛掉的,要让最小的质因子去筛,就不能提前删掉这个数。
代码实现
const int N=1e5;
int st[N],primes[N],pp=0;
//st标记数组 primes存储素数 pp当作指针
void ola(int x)
{
for(int i=2;i<=x;i++)
{
if(!st[i]) primes[++pp]=i;//如果st[i]没有被标记过,是素数,存储到primes
for(int j=1;primes[j]*i<=x;j++)
//枚举到第i个数时,筛去所有primes[i]*j,就删掉了所有已知素数倍
{
st[primes[j]*i]=1;//把倍数标记掉
if(i%primes[j]==0) break;//看不懂就先背下来,这是一个数学问题
}
}
return;
}
int main()
{
int n;
cin>>n;
ola(n);
}