素数筛法详解:埃氏筛和欧拉筛

主要讲解怎么判断一个数字是否是素数:

埃式筛

学习埃氏筛之前,我们先看一下暴力筛法,即对每个数都用试除法判断其是不是质数:

暴力筛法:

# include <stdio.h>

int main()
{
    int st[N]; // 初始化为0, 0表示质数,1表示合数

    for(int i = 2; i <= n; i++)
    {
	    for(int j = 2; j <= i / j; j++)
        {//试除法
	    	if(i % j == 0){
	    		st[i] = 1; // 合数,标记为1 
	    }
    }
}

这种方式无疑是最慢的,我们看一下如何加快,换一种思路:一个质数的倍数一定是合数,所以,假设P是质数,那么就可以筛选掉2~100之间所有P的倍数。

先看个例子,对于数列1~11:

先筛去2的倍数:

然后筛去3的倍数:

然后筛去5的倍数:

至此,1~11内的所有合数都被筛完了, 2 3 5 7 11是数列中的质数。

为什么这样能筛去所有的合数呢,因为一个合数一定能被分解为几个质数的幂的乘积,并且这个数的质因子一定是小于它本身的,所以当我们从小到大将每个质数的倍数都筛去的话,当遍历到一个合数时,它一定已经被它的质因子给筛去了

埃氏筛代码:

# include <stdio.h>
int main()
{
	int n = 100;
	int st[100] = {0};
	for (int i=2; i<=n; ++i)
	{
		if (st[i] == 0)
		{
			for (int j=2*i; j<=n; j=j+i)
				st[j] = 1;// j是i的一个倍数,j是合数,筛掉。
		}
	}
}


以上的时间复杂度为

我们还可以对其进行优化:

        1.我们会先筛2的所有倍数,然后筛3的所有倍数,但筛除3的倍数时,我们还是从3的2倍开始筛,其实3 * 2 ,已经被2 * 3时筛过了。

        又比如说筛5的倍数时,我们从5的2倍开始筛,但是5 * 2会先被2 * 5筛去, 5 * 3会先被3 * 5会筛去,5 * 4会先被2 * 10筛去,所以我们每一次只需要从i*i开始筛,因为(2,3,…,i - 1)倍已经被筛过了。

       2.另外,判断一个数是不是质数,我们只判断[2, sqrt(n)]内有没有它的因子。在筛合数的时候,我们也可以这样做,因为一个合数的最小质因子一定小于等于sqrt(n)

        解释:一个合数p一定是由一些质因子相乘组成,也就是

因此我们可以判断出来,一个合数的最小质因子一定存在质因子小于等于sqrt(n)

优化后的埃式筛:

# include <stdio.h>
# include <math.h>
int main()
{
	int n = 100;
	int st[100] = {0};
	for (int i=2; i<=int(sqrt(n)); i++)
	{
		if(st[i] == 0)
		{
			for(int j = i * i; j <= n; j += i)
			    st[j] = 1; // j是i的一个倍数,j是合数,筛掉。
		}
	}
}

优化后的时间复杂度可以近似看成O ( n ) 了。

但是,我们还可以更快,那就是欧拉筛。

欧拉筛

欧拉筛,又称为线性筛,时间复杂度为O ( n )

欧拉筛之所以能这么快,是因为不重复筛选,特点是合数都是被它的最小质因子筛去的

代码:

# include <stdio.h>
# include <stdio.h>

int main()
{
	int n = 100;
	int st[100];//用于表示i是否是素数,如果是则为1,不是则为0
	for (int i=0; i<100; ++i)
		st[i] = 1;// 初始化
	int primes[100];//用于存储素数。
	int k = 0;
	for (int i=2; i<=int(sqrt(n)); ++i)
	{
		if (st[i] == 1)
		{
			primes[k] = i;
			k = k + 1;
		}
		for (int j=0; j<k; ++j)
		{
			st[primes[j]*i] = 0; //筛去合数 
			if (i % primes[j] == 0) // 若 primes[j]是(合数)i的最小素因子,结束本轮遍历,防止重复标记 
				break;
		}
	} 
} 

欧拉筛的核心思想就是确保每个合数只被最小质因数筛掉。

可以参考对欧拉筛的解释视频:

欧拉筛,几行就行,一次就好_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1LR4y1Z7pm/?spm_id_from=333.337.search-card.all.click&vd_source=617dcf7ed08ed9625bdfcadc93c4fac7

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值