关于素数筛的一些总结

关于本篇博客

这是我的第一篇博客,大二,刚开始备赛蓝桥杯的c/c++组,就遇到了一道有关素数的题,那篇试题给出解答的作者提到了先用素数筛把素数选出来,用到了一个我陌生的算法,所以我下来看了关于素数筛的很多博客。

关于素数筛

我想在网上已经有很多很多博客都有关于素数筛选的方法了,总的来看我发现大的方向来说就三种,在这里归纳一下。

  • 直接暴力利用素数的定义筛除
  • 埃氏筛法及优化
  • 欧式筛法

第一种:定义法

这里引用百度百科上素数的定义,质数也就是素数,指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。

所以给了我们的一个比较直接能够想到的思路就是:我们如果能在小于它的数中找到它的一个除了1和0以外的因素,它就不是素数,反之则就是一个素数。

对于程序实现来说,这个算法也是非常的简单,下面贴上代码:

#include <stdio.h>
#define N 100000

void main(){
	int prime[N],cot=0;
	
	for (int n=2; n<N; n++){
		for (int i=2; i<n; i++)
			if (!(n%i)) break;
		if (i==n)  prime[cot++]=n;
	}
	
	printf("在%d以内一共有%d个素数\n",N,cot);
	for (int i=0; i<cot; i++){
		printf("%d ",prime[i]);
		if (!(i%10) && i!=0) printf("\n");
	}
}

第二种:埃式筛法

这种方法的原理是:把所求范围内的整数顺序排列,在1以后的,当前最小的数则是素数,然后再逐个剔除它的倍数,直到越界时结束,依次类推直到越界则剩下的全是素数。

这个算法的原理也是简单易懂,下面附上代码:

#include <stdio.h>
#include <string.h>
#define N 100000

void main(){
	int prime[N],isprime[N]={0},cot=0;
	for (int i=1; i<N; i++) isprime[i]=1;
	
	for(i=2; i<N); i++){
		if (isprime[i]){
			prime[cot++]=i;
			for(int j=2; i*j<N; j++){
				isprime[i*j]=0;
			}
		}
	}

	printf("在%d以内一共有%d个素数\n",N,cot);
	for (i=0; i<cot; i++){
		printf("%d ",prime[i]);
		if (!(i%10) && i!=0) printf("\n");
	}

}

但是在你跟着程序多走几步以后你就会发现它的一个比较大的缺陷。它会进行大量的重复剔除,例如当剔除2的倍数时,4,6,8,10,12…会被标记剔除,当剔除3的倍数时,6,9,12…会被标记剔除,我们能看到就短短几步,就有6和12被重复标记了。

我们再仔细观察,当剔除2的倍数时,我们已经把所有的偶数都已经剔除掉了,剩下的全是奇数,所以在后面剔除奇数的偶数倍时,都是在重复标记。所以提供了一个基本的思路就是:先把所有的除2以外的偶数直接标记剔除掉,然后再用大于2的奇数剔除它们的奇数倍即可。

代码部分只需在上面的基础上做一些简单的修改即可:

#include <stdio.h>
#include <string.h>
#define N 10000

void main(){
	int prime[N],isprime[N]={0},cot=0;

	for (int i=3; i<N; i+=2) isprime[i]=1;
	isprime[2]=1;

	for(i=2; i<N; i++){
		if (isprime[i]){
			prime[cot++]=i;
			for(int j=3; i*j<N; j+=2){
				isprime[i*j]=0;
			}
		}
	}

	printf("在%d以内一共有%d个素数\n",N,cot);
	for (i=0; i<cot; i++){
		printf("%d ",prime[i]);
		if (!(i%10) && i!=0) printf("\n");
	}

}

在这个基础上我们还能对算法进一步优化,就是关于倍数for循环的起始值问题,上面的代码是都是从3开始,但是我们如果跟着程序走几个数就会发现,在 i * (i-2k) 前,必有 (i-2k) * i 已经把它剔除过了,例如5*3,在3*5时就已经剔除过了。所以我们的起始值就可以设置为 i 本身,这样又可以再减少一些步骤。

#include <stdio.h>
#include <string.h>
#define N 10000

void main(){
	int prime[N],isprime[N]={0},cot=1;

	for (int i=3; i<N; i+=2) isprime[i]=1;
	isprime[2]=1; prime[0]=2;

	for(i=3; i<N; i++){
		if (isprime[i]){
			prime[cot++]=i;
			for(int j=i; i*j<N; j+=2){
				isprime[i*j]=0;
			}
		}
	}

	printf("在%d以内一共有%d个素数\n",N,cot);
	for (i=0; i<cot; i++){
		printf("%d ",prime[i]);
		if (!(i%10) && i!=0) printf("\n");
	}

}

在这里我发现上诉的这些方法都还可以进一步优化,比如3*15和5*9就还是重复,但是这里我还没有想到进一步的优化方法。大家可以下去自己尝试一下,也可以和我一起讨论一下。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值