埃氏筛法C++

本文详细介绍了埃氏筛法的基本原理及其应用,并通过实例对比展示了该算法相较于传统素数判断方法的优势,尤其在大规模数据处理时表现显著。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

主要我想讲讲 “ 碍事(埃氏) ” 筛法

(一)介绍

埃氏筛法

        这是一个我感觉和辗转相除法一样NB的算法。原理也很简单。

        首先将2到n范围内的整数写下来。其中2是最小的素数。将表中所有的2的倍数划去,

        表中剩下的最小的数字就是3,他不能被更小的数整除,所以3是素数。

        再将表中所有的3的倍数划去…… 以此类推,如果表中剩余的最小的数是m,那么m就是素数。

        然后将表中所有m的倍数划去,像这样反复操作,就能依次枚举n以内的素数,

        埃氏筛法的时间复杂度是0(n*log(logn))。

(二)对比

如果原理你都理解了 那么你直接去后边看用法

如果 你还没有理解  那么  我再举个例子

最朴素的判断素数方法 是这样的 对吧

int sushu(int n)
{
	for(int i = 2; i <= sqrt(n); i++)
	{
		if (i % n == 0)
		{
			return 0;//不是素数
		}
		return 1;//是素数
	}
}
//需要sqrt (n) 次循环才能判断出来n是素数

那么 现在我让你判断1-20 之内的素数,你会怎么做从1 遍历到20 挨个判断一下是不是素数 ,对吧?

那么我们来分析一下复杂度,你就认为我们算一下循环次数好了。

从1-20 需要遍历20次,对于每个数,最倒霉情况下 需要sqrt(n)次才完成能判断。

这一共是多少次呢 sqrt(2) + sqrt(3) + sqrt(4) + sqrt(5) + ......... sqrt(20) 次。

通过计算器计算得到:答案应该60,也就是需要60次。

那么我们再来看下,我们使用埃氏筛法需要几次:

for(int i = 2; i <= maxn/* 最大到哪 */; i++) //从2开始往后筛.
{
	if (sushu[i])
	{
		for(int j = i + i; j <= maxn; j += i)
		{
			sushu[j] = 0;
		}
	}
}

首先,外面的循环:毋庸置疑,从2-20遍历:需要20次(其实是19,我们忽略四舍五入为20)

我们来看里面的操作:从2开始,2是素数,那么4不是、6不是、8也不是、10、12、14、16、18、20 都不是,这是9次操作

然后3开始:6、9、12、15、18都不是素数:这是5次操作

然后4跳过,5跳过,6跳过

从7开始:14不是素数,这是1次

操作后边的8、9、10、11、12、14、15、16、18、20都会跳过,而11、13、17 、19都不会操作 因为他们的倍数大于20了。

那么一共进行了 20 + 9 + 5 + 1 = 35次操作,比那个60少了将近一半。

其实因为举的例子很小:只到20。所以你看到的才快了这么点。

你可以想一下:如果数字变得很大的时候,比如一千万,你用普通的筛法,是需要更多次数的。

所以,这个埃氏筛法快的其实不止这么一点~~(至于究竟快多少 先不管了 你只要知道很快就好了)

(三)下面说下   怎么用它

明白了原理用起来就很灵活了

比如:

例一     求1-20之间的素数个数

int main()
{
	int ans = 0;//用来计数
	bool sushu[21];
	for(int i = 0; i <= 20; i++)
	{
		sushu[i] = 1; //先全部置为真
	}
	sushu[0] = sushu[1] = 0;//1 0 不是素数

	for(int i = 2; i <= 20; i++) //从2开始往后筛
	{
		if(sushu[i])
		{
			for(int j = 2 * i; j <= 20; j += i)
			{
				sushu[j] = 0;
			}
		}
		if(sushu[i])
		{
			ans++;//如果是素数 就计数
		}
	}
	cout << ans;
	return 0;
}

例二    就是我们的原题

#include<iostream>
const int N = 5e6 + 10;//总的范围规定在这里
using namespace std;
bool su[N];
void sushu()//我们将这个埃氏筛法写成一个函数
{
	for(int i = 0; i <= N; i++)
	{
		su[i] = 1;//先全部置为真
	}
	su[0] = su[1] = 0;//1 0 不是素数
	for(int i = 2; i <= N; i++) //从2开始往后筛
	{
		if(su[i])
		{
			for(int j = i + i; j <= N; j += i)
			{
				su[j] = 0;
			}
		}
	}
}
int n, m;
int main()
{
	/*
	我们在程序刚开始先调用这个函数
	把这个 sushu 数组处理成我们想要的样子:用来判断素数。 
	这可能就是预处理的思想。
	我们在开头处理这一次把 sushu 数组里面下标是素数的全部变成了 "1" 
	后边想判断是不是素数直接用 sushu[i] 是不是真就好了
	*/
	sushu();
	int ans = 0;//用来计数
	cin >> n >> m;//输入头和尾 
	for(int i = n; i <= m; i++)//遍历头到尾,判断就行了 
	{
		if(su[i])
		{
			ans++;//如果是素数 就计数
		}
	}
	cout << ans;
	return 0;
}

例三    输入一个数n   判断他是不是素数(多组测试数据)

n的范围是 (n<1e6)

这个题很有意思:题上说有多组测试数据(没说几组)。

每次给你一个n那么其实意思就是说:有很多个n让你判断他们是不是素数而且 n 最大是1e6。

那么你们想一下:如果我第一个数给你1000000,你用普通的判断素数的方法:需要sqrt(n)次才能判断出来,

也就是差不多3000次才能判断出来是不是素数,对吧?

3000次也不多。那如果我输入一共一万组呢?就需要3000 * 10000次了吧,所以:这个时候就体现了 “预处理” 的重要性。

我们先预处理出来 1e6 以内的所有素数,这样不管你输入啥我直接去看是不是素数就好了,对吧?

那么 “预处理” 之前我也想到了,但是用最普通的筛法去做预处理。

那么你想一下:1e6!每个你都需要去判断一下是不是素数。

我刚才在上边算了 20 以内的素数需要 60 次才能判断出来。那1e6以内需要多少次呢?

我告诉你:大概需要1e9次(也就是十亿次)

而用埃氏筛法需要几次呢?我直接告诉你们:需要70万次。

十亿 和 七十万 差别不用我说了吧?

这就是这个算法的效率 也是算法的魅力所在。

#include<stdio.h>
#include<algorithm>
#include<math.h>
const int maxn = 1e6 + 7;//总的范围规定在这里
using namespace std;
bool sum[maxn];
void sushu()//我们将这个埃氏筛法写成一个函数
{
	for(int i = 0; i <= N; i++)
	{
		su[i] = 1;//先全部置为真
	}
	su[0] = su[1] = 0;//1 0 不是素数
	for(int i = 2; i <= N; i++) //从2开始往后筛
	{
		if(su[i])
		{
			for(int j = i + i; j <= N; j += i)
			{
				su[j] = 0;
			}
		}
	}
}
int n;
int main()
{
	sushu();//预处理

	//输入 n
	while(scanf("%d",&n)!=EOF)
	{
		if(sum[n])
		{
			printf("Yes\n");
		}
		else
		{
			printf("No\n");
		}
	}
	return 0;
}

(四)~~~~~

好像还有一个更厉害的叫欧拉筛法(线型筛法),我根本不知道那是啥啊

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值