素数筛
暴力法
对于一个数字x,可以从2枚举到n-1,进行mod运算时间复杂度为O(n)
#include<stdio.h>
bool findprime(int x)
{
if(x==1) return false;
if(x==2) return true;
for(int i = 2;i<x;i++)
{
if(x%i==0)
return false;
}
return true;
}
int main()
{
long long n,m;
long long a;
scanf("%lld %lld",&n,&m);
while(m--)
{
scanf("lld",&a);
if(findprime(a))
printf("Yes\n");
else
printf("No\n");
}
return 0;
}
暴力优化.:1. for 循环里面的 i<n改成 i<=sqrt(n) O(sqrt(n)/3)
因为一个数可以写成两个数相乘的形式 这两个数可以颠倒位置,结果相同因为for 循环从小的开始到大的结束,所以不用遍历到较大的因子以减少运算量。
-
利用判断(根据素数的规律):大于等于5的质数一定和6的倍数相邻,例如5 and 7,11 and 13,17 and 19,
令 x≥1 , 将 大 于 等 于 55 的 自 然 数 表 示 如 下 : …6x1,6x,6x+1,6x+2,6x+3,6x+4,6x+5,6(x+1),6(x+1)+1…
可以看到,不在6的倍数两侧,即6x两侧的数为6x+2,6x+3,6x+4…由于2(3x+1),3(2x+1),2(3x+2),
所以它们一定不是素数,再除去6x本身,显然,素数要出现只可能出现在6x的相邻两侧。
所以,基于以上条件,我们假如要判定的数为n,则n必定是6x-1或6x+1的形式,对于循环中6i-1, 6i,6i+1,6i+2,6i+3,6i+4,其中如果n能被6i,6i+2,6i+4整除,则n至少得是一个偶数,
但是6x-1或6x+1的形式明显是一个奇数,故不成立;另外,如果n能被6x+3整除,则n至少能被3整 除,
但是6x能被3整除,故6x-1或6x+1(即n)不可能被3整除,故不成立。综上,循环中只需要考虑6i-1 和6i+1的情况时间复杂度为 O(sqrt(n)/3)
bool findprime(int x) { if(x==1) return false; if(x==2 || x==3) return true; if(x % 6 !=1 && x%6 !=5) return false; for(int i=5;i<sqrt(x);i+=6) { if(x%i==0 || x(i+2)==0) return false; } return true; }
埃氏筛
基本思想 :素数的倍数一定不是素数。
利用已经找到的素数,从后面书中筛去当前素数的倍数,由算数基本定理可得,当前菽粟已经是晒取数的质因子,如此下去能筛出所有操作之后的合数,是一种比较快的筛法。
先把 2 表上 true 然后把2-100中2的倍数标位false,再把三标为true,把三的倍数标位false,重复操作完成检索。、
//某区间内有多少个素数
int prime[maxn] ; //存素数
bool is_prime[maxn] ; //存是否是素数
int isPrime(int x)
{ memset(is_prime , true , sizeof(is_prime)) ; //注意初始化
int p = 0 ; //计数器
is_prime[0] = false ;
is_prime[1] = false ; //0 , 1 不是素数
for(int i = 2 ; i <= x ;++ i)
{
if(is_prime[i])
{
prime[p ++] = i ;
for(int j = 2 * i ; j <= x ; j += i ) //如果i是素数,那么让i的所有 倍数都不是素数
is_prime[j] = false ;
}
return p ; //返回素数的个数
}
时间复杂度为 O(nloglogn)
但是研究上述代码,我们可以发现这个方法可以继续优化,因为在埃氏筛中,有的数字会因为有多个因 数而被筛很多次,例如30会被2,3,5筛,导致计算冗余,下面我们可以对埃氏筛进行改进,从而得到欧式筛
这个方法可以保证每个合数在N/M的最小素因子时被筛出,所以时间复杂度仅为O(N)
已经可以满足大部分需求,在竞赛中,这种方法也可以在极短的时间内求出关于N的质数表。
思想:最小质因数 * 最大因数(非自己) = 这个合数
//某区间有多少个素数
int prime[maxn];
bool boo[maxn];
int findprime(int x)
{int p=0;
memset (book,true,sizeof(book));
book[0]=false;
book[1]=false;
for(int i=2;i<=x;i++)
{
if(book[i])
{
prime[p++]=i;
}
for(int j=0;j<p &&prime[j]*i<=x;j++)
{
book[prime[j]*i]=false}
}
}