埃氏筛法与欧拉筛的原理

关于合数一定有质因子的证明

另外注明,素数即质数

	一个合数n之所以为合数,就是因为除1和本身之外,还至少有一个不等于n的素因子p。
    
	所以,一个合数n,如果存在大于1的正整数m和素数p,使n=m*p,则p为n的素因子,若m为合数,
则重复以上步骤求其余的素因子,直到n分解为素因子的乘积。

	例如256=2*2*2*2*2*2*2*2,510510=2*3*5*7*11*13*17.

也正是因为所有的合数一定有质因子,所以埃氏筛法和欧拉筛才是正确的

埃氏筛法的原理
从2开始,当我们遍历到一个素数时,把所有该素数的倍数(自然是合数)都筛选出来,进行标记
欧拉筛的原理

注:本段有参考某位大佬的文章

传送门

埃氏筛法的缺陷 :对于一个合数,有可能被筛多次。例如 30 = 2 * 15 = 3 * 10 = 5*6……那么如何确保
每个合数只被筛选一次呢?我们只要用它的最小质因子来筛选即可,这便是欧拉筛法。

所以欧拉筛是在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复的目的。

代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100;
int prime[maxn];
int visit[maxn];
int main(){
	int cnt=0;
    memset(visit,0,sizeof(visit));
    memset(prime, 0,sizeof(prime));
    for (int i = 2;i <= maxn; i++) {//打表测试一下 
        cout<<"当 i = "<<i<<"时"<<endl;
        if (!visit[i]) 
            prime[cnt++] = i;      //纪录素数, 这个prime[0] 相当于 cnt,用来计数
        for (int j = 0; j <cnt&& i*prime[j] <= maxn; j++) {
            cout<<"  j = "<<j<<" prime["<<j<<"]"<<" = "<<prime[j]<<" i*prime[j] = "<<i*prime[j]<<endl;
            visit[i*prime[j]] = 1;
/*
解释一:
prime[j]是当前已经确定的素数,而i*prime[j]计算的该素数的倍数 
此前有一点疑惑, i是外层循环的控制变量,有可能轮到这个素数的时候,i已经>2、3、4、5。。。了
就没办法计算这个素数的 2、3、4、5。。。倍了,但是其实是这样的,如果该素数前有素数
那么其实这两个素数的倍数已经被标记为合数了, 作为(比prime[j]小的素数)的倍数被标记了 
解释二: 
我们之前做埃氏筛法的优化时就已经知道了,循环标记时j的下标应该从i*i开始,原因见《计算机考研机试指南 》P96 
而在这里,我们得到一个新的素数的时候,prime[j]是通过i赋值给它得到的,那么其实i*prime[j]的初始值就相当于i*i 
也因此i是不会小于prime[j]的,也即i>=prime[j] 永真 
i如果是素数,那么i=prime[j]的最大值
i如果是合数,那么i>prime[j]的最大值 
*/
            if (i % prime[j] == 0) break;
/*
j是从小到大的, 那么prime[j]也是从小到大的,prime[j]里面存的都是素数,那么i对prime[j]求余能除尽,说明prime[j]是i的质因子 
那么i对prime[j]求余,遇到的第一个能除尽的数就是i的最小质因子
*/
        }
    }
}

《计算机考研机试指南 》P96 alt

以下是与本期主题无关的一个小知识
	在if(表达式){语句} 条件语句里,如果表达式值为真的话,刚执行花括号里的语句;
	若表达式为假,刚不执行。
    1. if(!i)
    	表达式为(false)假,等价于i==0;
    2. if(i)
    	表达式为(true)真,等价于i!=0。(可假设i=1)
    可以简单记为"假零 真一"(贾玲)
菜鸟成长记 文章被收录于专栏

根据自己的学习历程,记录该过程中的各种困惑以及解决困惑的方法

全部评论

相关推荐

11-24 11:23
门头沟学院 C++
点赞 评论 收藏
分享
美团 后端开发 总包n(15%是股票)
点赞 评论 收藏
分享
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务