两种素数的筛法(埃拉托斯特尼筛法与欧拉筛)

很多文章都已经介绍过这两类筛法了,在这不过多叙述,而这篇文章的重点放在“欧拉筛”的细节上(如果必要,可以直接去看“欧拉筛”那节)。
之前浏览过大篇文章,都是来细说 “i % prime[j]==0 ” 为什么跳出循环,而没有对 “为什么只需要当前的 i 乘以比它更小的 prime[j] 就可以筛去合数” 在后面的“欧拉筛”篇幅会有详细推导。

老规矩,先放上埃式筛:
int cnt = 0 从0开始记录素数个数
prime[cnt++] 用来存放质数
vis[i] 当值为1时,说明它是合数,反之当值为0时,说明它是质数。
0和1都不是质数,所以先筛去,然后从2开始遍历即可

int aishi() //埃拉托斯特尼筛法   原理:由于所有合数都由“质数乘以一个数”而来,所以筛去每个质数的所有倍数
{
	int cnt = 0;
	memset(vis, 0, sizeof(vis));
	vis[0] = vis[1] = 1;
	for (int i = 2; i <= MAXN; i++)
	{
		if (!vis[i])
		{
			prime[cnt++] = i;
			for (int j = 2 * i; j <= MAXN; j += i)
				vis[j] = 1;
		}
	}
	return cnt;
}


先说埃式筛的原理: 遍历到当前 i 为素数时,用prime数组加入它,然后筛去以 i 为因数的合数。即原理就是,由于所有合数都由 “质数乘以一个数” 而来,所以筛去每个质数的所有倍数,剩下的数即都是质数

现在重点放在欧拉筛上:
代码注解看不懂就看文字~~~

int oula() //欧拉线性筛    原理:由于所有合数都有一个最小质因子,所以筛去每个以“prime[j]为最小质因子”的合数
{
	int cnt = 0;
	memset(vis, 0, sizeof(vis));
	vis[0] = vis[1] = 1;
	for(int i=2;i<=MAXN;i++)
	{
		if (!vis[i])
			prime[cnt++] = i;//设当前比i更小的数为k,假定若有k乘以prime[j]筛去,由于prime[j]之前的数要么是质数要么是合数,如果是质数,他们乘积的合数会以“k”为最小质因子筛去;如果“k”为合数,说明“k”有一个最小质因子,且这个“最小质因子”一定是小于“k”的,那么也就是说,k*prime[j]的最小质因子不是prime[j]而是k,所以无论之前的”k“是质数还是合数,k*prime[j]的最小质因子都不会是prime[j],都不会以prime[j]为“最小质因子”筛去,而是之后会以“k”为最小质因子筛去
		for (int j = 0; j < cnt&&i*prime[j] <= MAXN; j++)
		{
			vis[i*prime[j]] = 1;
			if (i%prime[j]==0)   //若不跳出循环,说明以“prime[j+1]”为“最小质因子”来筛合数,导致之后的i==k*prime[j],i*prime[j+1]=prime[j]*k*prime[j+1],说明这个合数“i*prime[j+1]”的最小质因子为prime[j],说明之后当i==k*prime[j]时会以“当prime[j]为最小质因子”的条件筛去,所以在这时就可以退出循环,以免之后重复筛去
				break;
		}
	}
	return cnt;
}


由于任何一个合数都有一个“最小质因子”,那么欧拉筛的原理就是,筛去所有以 “prime[j]” 为最小质因子的合数。
上面代码中,重点当然是那个循环块了:

for (int j = 0; j < cnt&&i*prime[j] <= MAXN; j++)
		{
			vis[i*prime[j]] = 1;
			if (i%prime[j]==0)   
				break;
		}


大致流程: 当进入这个循环时,i是固定了的,遍历prime[j],将 i*prime[j] 筛去,如果发现 i为prime[j]的倍数,则退出循环。

1、
你还会发现一个顺序:如果当前 i 为质数,则加入prime数组,然后遍历prime数组时,这个刚刚加入的质数,它第一次与 i 相乘时的 i 等于它自己呀!
也就是说每个新加入的质数,第一个相乘的系数,是从它自己开始的! (重点!)

我们之前说了,将所有以当前 prime[j]为 “最小质因子” 的合数筛去。上面说过,代码中是 “从prime[j]开始的 i ” 与 prime[j] 相乘的,那么比prime[j]小的 i 为什么不乘prime[j] 呢,它们所相乘得到的合数在哪就被筛去了?

设有 k < i ,则有合数 k * prime[j] ,对k讨论:
当 k 为质数时: 由于 k < i,所以对于合数 k * prime[j] ,因为此时 i == prime[j] 的,而 k < i,所以 对于合数 k * prime[j] 的最小质因子是 k ,比prime[j] 小。
当 k 为合数时: 由于 k < i ,那么合数 k 一定有一个最小质因子,且这个最小质因子一定是小于 k 的!(合数的质因子不可能比自己还大吧?)所以对于合数 k * prime[j] 的最小质因子,一定是 k 的最小质因子(假设是 a ),当然 a 都小于 k 了,而 k < i ,由于此时 i == prime[j] ,那么这个合数的最小质因子肯定不会是 prime[j] 啦,且比它小。

综上所述:对于 k < i,无论 k 取何值,合数 k * prime[j] 的最小质因子都不会是prime[j],且一定比prime[j]还要小(重点!)
现在回到一开始想一想,欧拉筛原理是什么?筛去所有以 prime[j] 为最小质因子的 合数啊,如果此时合数 k*prime[j] 的最小质因子都不是prime[j],我还需要筛去吗?它必会被这个合数的最小质因子筛去呀(这个最小质因子小于prime[j])
所以说,虽然新加入的质数并没有与小于它的所有数相乘去筛去合数,却有更高效率的手段筛去它们。

2、
这部分当然就是那个 “break” 啦,反而这部分更好证明:
如果在 i % prime[j] == 0 时不退出循环,则此时 i = k * prime[j](k为正整数),则会筛去合数: i * prime[j+1] ,而这个式子化为:i * prime[j+1] = prime[j] * k * prime[j+1] ,在化简完后发现,这个合数的最小质因子是 prime[j] ,而并不是 prime[j+1] ,所以结论就是:当 i 遍历到 i= k * prime[j+1] 时,必会以更小的质数 prime[j] 相乘,再筛去这个合数,而不是以 “prime[j+1]” 为这个合数的最小质因子 筛去。


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值