目录
判断一个数是否是素数以及一个区间中素数的个数的问题,我们容易想到的是试除法。根据质数的定义,除1和它本身,没有能整除它的数,我们把这样的数称为素数或质数。例如:2、3、5、7、11......等等。
试除法
bool Isprimes(int n)//判断是否为素数
{
if(n<=1)return false;
for(int i=2;i<=n;i++)
if(n%i==0)return false;
return true;
}
其实很容易观察到我们只需要枚举到√n即可,因为因子都是成对出现的。例如28的因子:
1 | 28 |
2 | 14 |
4 | 7 |
因此,可以对试除法做出改进。
试除法改进
bool Isprimes(int n)//判断是否为素数
{
if(n<=1)return false;
for(int i=2;i*i<=n;i++)//枚举到根号n即可
if(n%i==0)return false;
return true;
}
当数据范围非常大的时候,试除法的时间复杂度非常高,很不好用。而埃拉托斯特尼筛法是比试除法更高效的算法。埃拉托斯特尼 (Eratosthenes) 筛法,简称埃氏筛,是一种由希腊数学家埃拉托斯特尼所提出的一种简单检定素数的 算法。为了得到自然数 n 以内的全部素数,必须把不大于根号 n 的所有素数的倍数剔除,剩下的就是素数。给出要筛数值的范围 n,找出以内的素数。先用 2 去筛,即把 2 留下,把 2 的倍数剔 除掉;再用下一个质数,也就是 3 筛,把 3 留下,把 3 的倍数剔 除掉;接下去用下一个质数 5 筛,把 5 留下,把 5 的倍数剔除 掉;不断重复下去...其时间复杂度O(nloglogn)
埃氏筛
bool st[N];//st[N]数组表示状态,为0是素数,为1不是素数
int cnt=0;//记录当前素数的个数
int isprime[N];//存储素数的数组
void Isprimes(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i])isprime[cnt++]=i;//未标记则是素数,记录下来
for(int j=i*2;j<=n;j=j+i)//一个数的整数倍不是素数,有其他因子存在
st[j]=true;
}
}
j也可以从i * i开始枚举,因在此之前,比i小的已经枚举过了。
埃氏筛改进1
void Isprimes(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i])isprime[cnt++]=i;//未标记则是素数,记录下来
for(int j=i*i;j<=n;j=j+i)//一个数的整数倍不是素数,有其他因子存在
st[j]=true;
}
}
也可以只枚举素数的倍数,素数的整数倍也一定不是素数,因为存在了除1和它本身以外的因子。
埃氏筛改进2
void Isprimes(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i])
{
isprime[cnt++]=i;//未标记则是素数,记录下来
for(int j=i*2;j<=n;j=j+i)//一个数的整数倍不是素数,有其他因子存在
st[j]=true;
}
}
}
在埃氏筛中,对于6这个数字,可以发现它被2和3都枚举了一次,造成时间上的浪费,而欧拉筛就在此进行了改进:让每个合数只被它的最小质因子筛选一次 。因此每个数只被枚举一次,故欧拉筛的时间复杂度O(n)。
欧拉筛
void Isprimes(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i])prime[cnt++]=i;
for(int j=0;prime[j]<=n/i;j++)
{
st[prime[j]*i]=true;
if(i%prime[j]==0)break;
}
}
}