栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

埃氏筛 C++

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

埃氏筛 C++

        在 求指定范围内的质数个数 问题中,一般有试除法和筛法两大类。

        试除法【时间复杂度为O(n^2)】容易超时。筛法中又有朴素筛、埃氏筛、欧拉筛。虽然欧拉筛【时间复杂度为O(n)】是线性的最优的,但是在理解和写比较复杂。一般用埃氏筛【时间复杂度为O(n loglogn)】就够了,埃氏筛代码简洁、更易理解。且本篇的埃氏筛还有两处细节优化。

埃氏筛原理

        先假设每个数都是质数。

从 2 开始,2是质数,那么2的倍数:4、6、8、10、12、14、16... 肯定不是质数3是质数,那么3的倍数:6、9、12、15、18、21..... 肯定不是质数4已经被筛去了(即被置为false),不是质数,那么4的倍数肯定被它的因子筛过,所以4不用看,跳过... ...

        没有被筛去的一定是质数,因为它没有被比它小的任何一个数筛去,说明它不是任何一个数的倍数,所以一定是质数。

代码
#include 
using namespace std;
#include       //Dev C++下memset头文件,vs 2019中不用加

const int N = 1e5;		//即 10,0000
bool isPrime[N + 5];	//标记每个数,如果为true,则为质数,否则为合数;这里一定要多开一个空间,否则标记N时,会数组越界而报错
int primes[N + 5];		//存储质数
int cnt = 0;			//记录质数个数

void aiPrime()
{
	memset(isPrime,true,sizeof(isPrime));		//先都初始化为真

	for (int i = 2;i <= N / i;i++)		        //注意这里 i <= N / i就可以,解释见后文
	{
		if (isPrime[i])		//如果当前数是质数,那么将它的倍数都标记为 false
		{			
			for (int j = i * i; j <= N; j += i)
			{
				isPrime[j] = false;
			}
		}
	}

	for (int i = 2; i <= N; i++)		//将质数放入primes数组
	{
		if(isPrime[i])
			primes[cnt++] = i;
	}
}

int main()
{
	aiPrime();
	cout << cnt << endl;
	return 0;
}

细节优化 

优化1

        现在我们来讨论一下为什么在筛去倍数时,只要 i <= N / i 就可以。

        如果是 i <= N,肯定是可以的。那么现在来看 i <= N / i,

        以当前质数 i = 109 为例,109的倍数 2 * 109 =218、3 * 109 = 327、4 * 109 = 436、5 * 109 = 545、6 * 109 = 654、7 * 109 = 763、8 * 109 = 872、9 * 109 = 981 这八个数都会被筛去,但是在 2、3、4、5、6、7、8、9 的时候就会被筛掉。

        如果 a * b = c (a <= b),那么 a <= sqrt(c) <= b。 所以 c 既会被 a 筛掉,又会被 b 筛掉,所以 c 一定会被 a 筛掉,而不用考虑 b。故 i <= sqrt(c) 就可以了。

        有的写法为 i * i <= c,是不推荐的。因为 i * i 容易溢出。虽然换成 除法 是比较慢的,但是很安全。所以 i <= N / i 和 i <= sqrt(N) 这两种写法都是可以的。

【ps: 有的地方写作 i <= sqrt(N) + 1或者是 i <= N / i + 1,其实是大可不必的】

        以N = 143为例,按照如上写法,那么i = 12 也参与循环,12的倍数【24、36...12*11 = 132】都会被筛去,可是这些注定被之前筛过。

        以N = 169为例,按照如上写法,那么i = 14 也参与循环,14的倍数【28、42...14*12 = 168】都会被筛去,可是这些注定被之前筛过。

        以N = 197为例,按照如上写法,那么i = 17 也参与循环,17的倍数【34、51...17*14 = 187】都会被筛去,可是这些注定被之前筛过。

优化2

        在取倍数时,不是从2倍(i * 2)开始,而是 for (int j = i * i ; j <= N; j += i) 。

应用

找质数(计蒜客:2019 蓝桥杯省赛 B 组模拟赛(一))  测试平台:计蒜客

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/779045.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号