判断素数-埃氏筛法的更优化,欧拉筛法的详解

这个线性复杂度的欧拉素数筛法,爱了爱了

今天讲一下关于欧拉筛法的原理和代码实现,实不相瞒,我也才刚get到这个筛法的点,乘着记忆清晰来教一遍梳理一下思路。

我查阅资料的时候也在很多博客和公众号上看到关于欧拉筛法的解释和代码实现,然后想学习了之后用我自己的话重新描述一遍,希望我的角度能让你有所收获!

欧拉筛法与埃氏筛法一样,都是围绕【素数的倍数不是素数】这个核心原理来展开的,有关埃氏筛法请移步我的上一篇博客“埃氏筛法的详解”,但是它比埃氏筛法更高效,当数据量足够大的时候,欧拉筛法的时间是埃氏筛法时间的十分之一甚至更少

现在我们正式展开欧拉筛法的学习。

欧拉筛法效率高的一个重要原因是,它不会重复标记一个数是不是素数的倍数,例如:6是素数2的倍数,同时也是素数3的倍数,欧拉算法能做到只标记一次6。

我们先看一下代码

#include <stdio.h>
#include <string.h>
using namespace std;

int main()
{
    int n, cnt = 0; // n是你要找的素数范围,cnt代表在这个范围内素数的个数

    int prime[100001];// 用来保存素数,注意,是用数组的值而不是用下标保存哦!

    bool visited[100001];// 就是这个数组!用来记录一个数是否是某个素数的倍数,标记它
    
    scanf("%d", &n); // 输入你想查找的范围

    memset(visited, false, sizeof(visited));// 初始化 

    memset(prime, 0, sizeof(prime));// 初始化
    
    for(int i = 2; i <= n; i++)

    {

        if(!visited[i])// 如果没有被标记过

        {
            prime[cnt++] = i;// 那么它就是素数啦~,存起来存起来,cnt记得+1 
        }

        for(int j = 0; j<cnt && i*prime[j]<=n; j++) // 这个循环是最难理解的部分,我将在下文详细解释

        {

            visited[i*prime[j]] = true;// 标记素数倍数

            if(i % prime[j] == 0) break;// 这一步跳出循环是欧拉筛法高效率的关键,也是同一个合数只被标记一次的关键

        }

    }
    return 0;
}

ok,代码如上,可以直接拷贝过去试试哦!

现在来详细分解一下那个for循环代码

此处我们暂时抛开 if(i % prime[j] == 0) break; 这句代码来讨论


for循环的循环变量 j 每次都从 0 开始跑,而这个 j 是从 prime[] 数组中取数据的,我们就可以这样等价,for循环每次都从已经找到的素数中从头开始取值。
例如 prime[] 中已经判断出了 2,3,5,7 那么for循环第一次取值2,如果没有跳出的话,第二次取值3,然后没有跳出的话就一直循环到已经找出的素数末尾。

然后

visited[i*prime[j]] = true;

这个代码段,是用来标记素数倍数的,例如,当 i 等于2的时候,素数数组里只存在一个素数 2 ,然后进入 for 循环,for 循环会将 2 取出来与 i 相乘,得到 2 的倍数 4 ,标记它,然后结束 for 循环。跑过 i = 3,执行到 i = 4 的时候,千万不要以为这个时候 for 循环不执行了! 这个时候 for 循环依旧会尽职地把 2 取出来和 4 相乘,将 2 的倍数 8 标记!然后 不会取到第二个素数 3 ,因为 4 % 2 == 0 -------

纵观之,如果我们不把

if(i % prime[j] == 0) break;

这段代码放在内的话, for 循环的作用就是将素数的倍数都标记上。


讨论关键性代码段 if(i % prime[j] == 0) break;

然后我们开始讨论上面那一小段代码的关键性作用:

18 是素数 3 的倍数,但是我们并不会用 3 的倍数来标记掉 18 , 因为假如我们用 3 来标记 18 ,那么 i 势必要跑到 6 这样 i * 3 才会标记掉18, 但是在这之前 i 就已经被 3 之前的一个素数 2 弄死了,退出循环了。
那么为什么要这么做?因为 6 既然能被 2 整除, 那么 18 迟早会被 2 标记,所以这里就不要再用 3 来重复标记 18。
你问我怎么知道 18 迟早会被 2 标记的? 6 能被 2 整除,那么 3 乘 6 就可以分解为 3 乘 2 乘 3, 也就是 2 乘 9 , 2 乘 9 就标记掉了18.


如果你看懂了上面的代码,我们再来总结一下

有个规律,不知道大家发现没有,凡是 i 跑到偶数位的时候,它永远只能与素数 2 相乘一次来标记掉一个数,然后因为能整除 2 而退出循环。 从这个规律,这个筛法的时间复杂度就可见一斑了。
外层 i 循环用来选出素数和作为标记素数倍数的倍数因子,内层 j 循环用来从素数中最小的开始遍历作为标记素数倍数的素数因子。然后通过 i % 素数 == 0 来退出标记避免重复标记。

谢谢你的观看!
也可以转到我的个人博客上去观看。
http://hsluoyang.club/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,135评论 6 514
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,317评论 3 397
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,596评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,481评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,492评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,153评论 1 309
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,737评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,657评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,193评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,276评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,420评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,093评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,783评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,262评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,390评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,787评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,427评论 2 359

推荐阅读更多精彩内容