数论基础——素数算法&素数筛(模板)

素数算法以及素数筛

基础版求素数

一个写起来很快的O(sqrt(n))算法,适用于大部分判断一个数是否是素数的题。

代码
int is_prime(long long m)
{
	long long i;
	for (i=2; i*i<=m; ++i)
	 {
		if (m%i==0) return 0;
	}
	return 1;
}

进阶版求素数

但某些题O(sqrt(n))解决不了,需要更好的O(sqrt(n)/3)。

算法原理

1.先看一个素数分布的规律:
大于等于5的素数,一定是出现在6的倍数的两侧(但是不代表在6的倍数的两侧的数一定是素数)。例如:5、7是素数,11、13也是素数,而25不是素数。
2.证明这个规律:
所有的大于等于5的数一定是{6x、6x+1、6x+2、6x+3、6x+4、6x+5}中的一个,其中6x+1和6x+5是在6的倍数的两侧。现在证明其他四个都不可能是素数。
证明:
6x、6x+2、6x+3、6x+4可以写成6x、2(3x+1)、3(2x+1)、2(3x+2)。显然他们都不是素数

代码
int is_prime(int n)
{
	long long i;
	if(n==1||n==4) return 0;
	if(n>=5)
	{
		if(n%6==1||n%6==5)
		{
			for(i=5;i*i<=n;i+=6)//采用了素数筛的思想(文章下半部分会介绍)
			{
				if(n%i==0||n%(i+2)==0) return 0;
			}
		}
		else return 0;
	}
	return 1;
 } 

素数筛

适用于求一段范围内的素数以及素数个数。
经典题型:
1.求n之前的所有素数
2.求[a,b]中前n个素数

原理

1.如果采用利用文章中第一个O(sqrt(n))的算法,进行n次循环即可得到答案。显然,这样O(nsqrt(n))肯定超时,所以我们需要进行优化。
2.如果我们判断出a是素数,那么我们就可以确定a的倍数都是合数。因此可以将这些倍数删除,这样就可以去掉一些不必要的判断。
3.进一步,我们会发现有些合数我们进行了多次删除。例如:对于合数6,它是2的倍数,也是3的倍数,所以我们进行了两次删除,因此我们可以进一步优化。
我们需要确保每一个合数只能被最小质因子筛掉。这一步我们通过核心代码if(i%prime[j]==0)break;实现。(在代码之后解释)

代码
#define MAXN 100000100 
int num[MAXN];
int prime[MAXN];//储存得到的素数 ,但是prime[0]是记录素数个数 
int get_prime()
{
	memset(num, 0, sizeof(num));//初始化
	for(int i=2;i<MAXN;i++)
	{
        /*num[i]==0即为素数*/	
		if(!num[i]) prime[++prime[0]]=i;
		
		for(int j=1;j<=prime[0]&&prime[j]<=MAXN/i;j++)
		{
			num[i*prime[j]]=1;//此时,prime[j]始终是i*prime[j]的最小质因子
			if(i%prime[j]==0) break;
		}
	}
 } 

注释
 if(i%prime[j]==0) break;
//如果i%prime[i]==0满足,
//则代表i*prime[j+1]能够用一个更小的素数和更大的合数相乘得到,因此跳出循环
//可以理解为ab%a==0,所以ab*c(a<c)能够用bc*a来表示
//例如:当i=6时,num[6*2]=1,而6%2=0,所以有6*3=18=9*2
另一个模板

来自kuangbin的模板,用一个数组,节省了空间,但难度大一点

const int MAXN = 1e7 + 5;
void getPrime(){
	memset(prime, 0, sizeof(prime));
	for (int i = 2;i <= MAXN;i++) {
		if (!prime[i])prime[++prime[0]] = i;
		for (int j = 1;j <= prime[0] && prime[j] <= MAXN / i;j++) {
			prime[prime[j] * i] = 1;
			if (i%prime[j] == 0) break;
		}
	}
}
发布了13 篇原创文章 · 获赞 2 · 访问量 343

猜你喜欢

转载自blog.csdn.net/weixin_45718149/article/details/104582147