当你要筛选出2~n之间的质数时,除了线性筛、MR素数判断等方法以外,还有一种叫做除余法的算法。
除余法的优点
时间复杂度:O((n√n)/3)
空间复杂度:较低
筛出2~n之间的素数比较暴力的方法,自然就是暴力枚举加上O(√n)判断,代码如下:
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int> Prime;
bool Is_prime(int x)//O(√n)判断
{
if(x<2) return false;
for(int i=0;i<Prime.size()&&Prime[i]*Prime[i]<=x;i++) if(!(x%Prime[i])) return false;
return true;
}
int main()
{
scanf("%d",&n);
for(int i=2;i<=n;i++) if(Is_prime(i)) Prime.push_back(i);//暴力枚举
for(int i=0;i<Prime.size();i++) printf("%d ",Prime[i]);
return 0;
}
其中的Is_prime()函数枚举了2~√n之间的全部质数来判断n是否为质数,这也是一个比较快速的判断是否为质数的函数。
这篇文章要讲的除余法,其实优化的并不是判断是否为质数的函数,而是调用函数的次数。
首先,我们要证明一点:任意一个大于3的质数p都可以用6n+1或6n+5的形式来表示(n为自然数)。
证明见下:
对于任意一个自然数,都可以表示为6n或6n+1或6n+2或6n+3或6n+4或6n+5(n为自然数),所以我们要进行分类讨论。
当p=6n时,很显然是一个合数
当p=6n+1时,无法确定其是否为合数
当p=6n+2时,可以表示为2(3n+1),很显然是一个合数
当p=6n+3时,可以表示为3(2n+1),很显然是一个合数
当p=6n+4时,可以表示为2(3n+2),很显然是一个合数
当p=6n+5时,无法确定其是否为合数
综上所述,若p为大于3的质数,则p必定可以用用6n+1或6n+5的形式来表示(n为自然数)
证明了这个,就可以直接推导出除余法了:
建立一个指针i,一开始指向5,随后依次增加2,4,2,4......直至i>n。对于每一个i,用其向素数集合中每一个小于√i的数取模,若能被整除,则i为合数,若每一个数都不能整除,则将i加入素数集合中。
具体实现过程中记得初始化:若n>=2,则将2加入素数集合;若n>=3,则将3加入素数集合。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int> Prime;
bool Is_prime(int x)//O(√n)判断
{
if(x<2) return false;
for(int i=0;i<Prime.size()&&Prime[i]*Prime[i]<=x;i++) if(!(x%Prime[i])) return false;
return true;
}
int main()
{
scanf("%d",&n);
if(n>=2) Prime.push_back(2);
if(n>=3) Prime.push_back(3);
int i=5;
while(i<=n)//枚举每一个i
{
if(Is_prime(i)) Prime.push_back(i);
if((i+=2)>n) break;//将i增加2
if(Is_prime(i)) Prime.push_back(i);
i+=4;//将i增加4
}
for(int i=0;i<Prime.size();i++) printf("%d ",Prime[i]);
return 0;
}
注:如果您通过此文学会了除余法,请您点个赞再离开。当然,也欢迎在讨论区指出此文的不足处,作者会及时对此文加以修正
版权声明:转载请注明地址