怎么从浩瀚的数中筛出素数,这是个问题,但是首先我们应该学习他的本质再去了解方法。
什么是素数
(本小段需要小学五年级以上数学知识)
素数:
素数又称质数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数(规定1既不是质数也不是合数)。
所以说素数其实很好判断,正整数n,如果用2到根n之间的所有整数去除,均无法整除,则n为质数。
那么该如何筛选素数呢?
真的真的真的很朴素的朴素筛
不难发现,很多最好想到的数学定理其实都是从定义出发,本想法也是一样的,所以说我们只需要在[2,n)这样一个区间里去遍历就可以了,如果是的话就sum ++,看代码:
//朴素筛
bool judge(ll x)
{
if(x==2)
return true;
if(x<2||x%2==0)
return false;
for(int i=3;i<=sqrt(x+1);i+=2)
{
if(x%i==0)
return false;
}
return true;
}
复杂度O(n√n)
欧拉筛
这是一种很好的线性筛法,欧拉筛法只筛除一次。
//欧拉筛
int prime[maxn];
int factor[maxn];
int Prime(int n)
{
int p=0;
for(int i=2;i<=n;i++)
{
if(!factor[i])
{
prime[p++]=i;
factor[i]=i;
}
for(int j=0;j<p&&prime[j]*i<=n;j++)
{
factor[prime[j]*i]=prime[j];
if(!(i%prime[j]))
break;
}
}
return p;
}
复杂度O(n)
埃氏筛
利用当前已经找到的素数,从后面的数中筛去当前素数的倍数,由预备知识一可知,当前素数已经是筛去数的质因子,如此下去能筛除所有之后的合数
//埃氏筛
int prime[maxn];
bool is_prime[maxn];
int Prime(int n)
{
memset(is_prime,true,sizeof(is_prime));
int p=0;
is_prime[0]=is_prime[1]=false;
for(int i=2;i<=n;i++)
{
if(is_prime[i])
{
prime[p++]=i;
for(int j=2*i;j<=n;j+=i)
is_prime[j]=false;
}
}
return p;
}
复杂度O(nloglogn)
区间筛(转载Prudento 的博文,表示感谢)
eg: 给定整数 a和b,请问区间[a,b)内有多少个素数
a<b<=10^12
b-a<=10^6
观察上面的例题,区间两边都是不定的。上面介绍的埃氏筛法解决了快速筛出2~n区间的所有素数。那么当左右区间都是不定时,如何才能高效筛选出素数?那么这里便要用到区间筛法。
区间筛法:
已知,对于任意合数b,其最小质因子<=根号b。如果根号b以内的素数表,便可以把埃氏筛法运用到区间[a,b)上了。也就是说,先分别做好[2,根号b)上的表和[a,b)上的表,然后从第一个表里筛得素数得同时,也将其倍数从第二个表中划去,因为b是右区间临界点,sqrt(b)显然是最大的质因子的上界,所以a~b这个区间里边的合数只可能被小于等于根下b的素数筛掉,最后剩下得便是所求区间内得素数。
原文链接:https://blog.csdn.net/Prudento/article/details/123956422
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define MAX_L 1000007
#define MAX_SORT_B 1000007
int is_prime[MAX_L];
int is_prime_small[MAX_SORT_B];
//对区间[a,b)内的整数执行筛法。isprime[i - a]=1 <=> i是素数
void segment_sieve(LL a,LL b)
{
for(int i=2; (LL)i*i < b; i++)is_prime_small[i]=1;//初始化 [2,sqrt(b)))
for(int i=0; i<b-a; i++)is_prime[i]=1;//初始化 [a,b) 但是a和b的范围很大,通过b-a压缩区间
for(int i=2; (LL)i * i<b; i++)筛[2,sqrt(b))
{
if(is_prime_small[i])
{
for(int j=2*i; (LL)j * j < b; j += i)//如此写比sqrt更快且防溢出
{
is_prime_small[j]=false;
}
//同时筛 [a,b)
//筛至少从i的2倍开始,2LL即把2转为LL型
//假如区间为[10,20),i=2时,(a+i-1)/i,即(10+2-1)/2=5 那么应该从2的五倍开始筛,此后同理。
for(LL j=max(2LL, (a+i-1)/i)*i ; j<b; j+=i) //(a+i-1)/i为[a,b)区间内的第一个数至少为i的多少倍.
{
is_prime[j - a] =false;//筛[a,b)
//因为初始化时通过b-a压缩区间.把a~b 压缩成0~b-a-1.所以is_prime[i]则表示数a+i,同理,j-a也是压缩区间。
//如a+1 是is_prime[1],下标为a+1-a。同理,a+j的下标便是j-a。
}
}
}
}
int main()
{
long long a,b;
while(~scanf("%lld %lld",&a,&b))
{
segment_sieve(a,b);
int cnt=0;
for(int j=0; j<b-a; j++)
{
if(is_prime[j])cnt++;
}
if(a==1)cnt--;
printf("%d\n",cnt);
}
return 0;
}
廿四筛
自我发明
总结
数论板子持续更新
真好玩ye~~