本文会从最基础的知识讲起,一步步深入,使文章更易于理解。

首先,对于判断一个数n是否是素数,我们有多种方法,最容易理解的即枚举2 ~ n - 1之间的所有数,若存在数x能整除n(即 n % x == 0),则n不是素数,否则n是素数。该方法的时间复杂度为O(n).

bool isPrime(int n){for(int i = 2; i < n; i++){if(n % i == 0)return false;//n能被i整除,则n不是素数}return true;
}

那么能不能对以上的判定素数的方法进行优化呢?答案是可以的!

我们知道若存在两个大于等于2的正整数x, y满足 x * y == n, x <= y。则有 n % x == 0,n % y == 0。且x <= sqrt(n),y >= sqrt(n),因为如果x和y都小于sqrt(n),则x * y必定小于n;反之,如果x和y都大于sqrt(n),则x * y必定大于n。所以,可以整除n的数是成对出现、一大一小分布在根号n两边的,如果我们找遍2 ~ sqrt(n),都找不到数可以整除n的话,则n必定是素数。改进后的时间复杂度为O(n ^ 1/2)。

bool isPrime(int n){int t = sqrt(n);for(int i = 2; i <= t; i++){if(n % i == 0)return false;}return true;
}

那么问题来了,如果我给你一个数字n,让你判断有多少个小于n的素数,即力扣的第204题,你该怎么办呢?

理所当然应该想到可以从2到n,对每个数进行判断,从而输出素数的个数。

但这样做的时间复杂度太大了,如果n的数量级达到10^5甚至更高,则所需的计算时间是无法接受的。

所以引出了埃氏筛法,时间复杂度为O(nloglogn)

该方法的核心思想是:从2开始,将每个质数的倍数都标记为合数,以达到筛选素数的目的。

int countPrimes(int n) {bool* isprime = new bool[n]();int num = 0;for(int i = 2; i < n; i++){if(!isprime[i]){num++;for(int j = 2 * i; j < n; j += i)isprime[j] = true;}}return num;
}

上述代码实现时,每当isprime[i]的值为false时,则i为素数,i的倍数都为合数,所以我们将i的倍数置为true,表示其不是素数。

为什么当遍历到i且isprime[i]为false时即可确定i为素数呢,难道不会漏筛吗?首先我们第一个遍历的数为2,isprime[2]为false,2是素数。所以当i == 2时,isprime[i]为false,i为素数成立。那么对于任意的i > 2,当我们遍历到i时,则我们一定先遍历了[2, i - 1]之间的所有数,若isprime[i]为false,则证明i不是[2, i - 1]中任意素数的倍数,即i只能被1和它自身整除,所以i为素数。

上述的埃氏筛还可以做一些优化,可以将j = i * 2 改为 j = i * i。若 x ∈ [2, i - 1],则 x * i 一定已经被标记为合数了,因为在遍历到i之前,我们一定已经遍历过x了,若x的最小质因数为k,k * y == x,则x的i倍即k的 i * y倍若小于n则一定已经被标记过了。

虽然埃氏筛可以极大的提高效率,但当数据量达到10^7时,它的速度仍然很慢。因为埃氏筛会存在重复筛除的情况,例如12会被2和3筛除,由于2 * 6已经将12筛除,则3 * 4筛除12时属于无用功,一个数有多少质因子就会被筛除多少次,这里面存在着大量的资源浪费。那么如何避免重复筛除从而提高效率呢?欧拉筛可以解决这一问题。

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

int countPrimes(int n) {bool* isprime = new bool[n]();vector<int> prime(1);for(int i = 2; i < n; i++){if(!isprime[i]){prime[0]++;prime.push_back(i);}for(int j = 1; j <= prime[0] && prime[j] * i < n; j++){isprime[prime[j] * i] = true;if(i % prime[j] == 0)break;}}return prime[0];
}

从2到n - 1遍历,当我们遍历到i时,如果isprime[i]为false,则i为素数,这个条件在i == 2时是成立的,于是我们接着将当前已经得到的素数的i倍标记为合数,直到 i % prime[j] == 0 (假设此时i / prime[j] = p)时退出循环,因为如果此时不退出,我们将prime[j + 1] * i标记为合数时是一种重复标记,我们令u = prime[j + 1] * i = prime[j + 1] * prime[j] * p,则可知u的最小质因子是prime[j],我们不应当用prime[j + 1]的i倍来标记,而应当在i增加到i == prime[j + 1] * p时,用prime[j]的i倍来筛除u,这样u才是被它的最小质因数筛除的。

上面我们说明了欧拉筛不会重复筛,那么为什么欧拉筛不会漏筛呢?

假设我们要筛掉数a,且a的最小质因数为p1,令a = p1 * b。那么显然b < a,b先被外层循环碰到。现在从小到大遍历prime中的素数,并将它们的b倍标记为合数。因为p1是a的最小质因数,所以b的最小质因数必不小于p1(a的最小质因数一定小于b的最小质因数,因为a是b的倍数,所以b的质因数也是a的质因数),这样就保证p1 * b筛掉a前不会跳出循环。即使b的最小质因数等于p1,也会先筛掉a后再退出循环。令a等于全体合数,就保证了每个合数都会被筛掉。

欧拉筛和埃氏筛是否会漏筛问题其实都可以用数学归纳法来证明,有兴趣的可以自己去了解。

素数筛(埃氏筛、欧拉筛)相关推荐

  1. 素数筛法(主要是欧拉筛)

    从2021.9.5日起,每天写1~2篇博客 埃氏筛法核心就是筛掉每一个素数的倍数,比方说2能筛4,6,8:但是有可能有重复,比如6可以被2,3筛掉.因此-->重点说的欧拉筛就有用了. void ...

  2. 埃氏筛与欧拉筛(线性筛)

    目录 一.前言 二.埃氏筛与欧拉筛(线性筛) 1.问题描述 2.基本思路 (1)埃氏筛法 (2)欧拉筛法 三.题例 1.上链接 2.简单思路 3.代码 (1)埃氏筛python版 (2)欧拉筛pyth ...

  3. 筛选质数,埃氏筛和欧拉筛(线性筛)

    求len之内的所有的素数 除了比较常用的开根号的求法,还有两种更好的方法,埃氏筛和线性筛.其中埃氏筛更好理解,而线性筛(欧拉筛)不好理解但是更快. 埃氏筛 #include <bits/stdc ...

  4. 欧拉筛 筛法求素数 及其例题 时间复杂度O(n)

    埃式筛法尽管不错,但是确实做了许多无用功,某个数可能会被重复的筛好几次,欧拉筛解决了这个方法,下面为代码: 注意理解if(i%prim[j]==0) break; 大佬讲的不错的博客,我就不做复读机了 ...

  5. Enlarge GCD CodeForces - 1034A(欧拉筛+最大公约数)

    题意: 给出n个数,删去其中一些使得总的gcd(最大公约数)最大 题目: Mr. F has n positive integers, a1,a2,-,an. He thinks the greate ...

  6. 素数筛选法(埃氏筛 欧拉筛)

    质数筛选法 文章目录 质数筛选法 前言 一.埃氏筛 O(nloglogn)O(nloglogn)O(nloglogn) 二.欧拉筛O(n)O(n)O(n) 总结 前言 当需要大范围内的素数时,例如1e ...

  7. 素数的线性筛法java,埃氏筛 线性筛(欧拉筛) 算法解析

    埃氏晒 埃拉托斯特尼筛法,简称埃氏晒,是一种用来求自然数n以内的全部素数. 他的基本原理是,如果我们要获得小于n的所有素数,那就把不大于根号n的所有素数的倍数剔除. 埃氏晒的原理很容易理解,一个合数, ...

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

    文章目录 摘要 埃式筛 欧拉筛 超级详细的基础算法和数据结构合集: https://blog.csdn.net/GD_ONE/article/details/104061907 摘要 本文主要介绍埃氏 ...

  9. 埃氏筛 线性筛(欧拉筛) 算法解析

    埃氏晒 埃拉托斯特尼筛法,简称埃氏晒,是一种用来求自然数n以内的全部素数. 他的基本原理是,如果我们要获得小于n的所有素数,那就把不大于根号n的所有素数的倍数剔除. 埃氏晒的原理很容易理解,一个合数, ...

  10. 算法笔记--素数筛(朴素筛,埃式筛,欧拉筛)

    素数 素数也叫质数,是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数.如2 , 3 , 5 , 7 , 11等. 素数筛 素数筛即筛选出1~n内的素数的方法,这里介绍三种 常见的求素数 ...

最新文章

  1. 【Groovy】Groovy 动态语言特性 ( Groovy 语言与 Java 语言执行效率对比 | 以动态特性编译的 Groovy 类 | 以静态特性编译的 Groovy 类 )
  2. Codeforces Round #FF
  3. 8.4 Change Reference to Value(将引用对象改为值对象)
  4. IT职场人生系列之七:学外语
  5. 下面对html描述正确的有多选题,YS - 《网页制作》复习题(多选题)
  6. 线性表的顺序存储结构和链式存储结构的比较
  7. 随机画五十个圆python_随拼音,随部首,随笔画,随组词,随解释,随笔顺,随词语,随康熙字典解释,随说文解字解释 - 查字典|CHAZIDIAN...
  8. Ubuntu下Gnome修改键盘映射
  9. 超星高级语言程序设计实验作业 (实验02 分支与循环程序设计)(一)
  10. 一种计算机显卡保护装置,一种计算机显卡辅助支撑装置制造方法及图纸
  11. iPhone13如何设置卡1和卡2收发信息
  12. python变量四则运算_python_第一次作业 四则运算
  13. 一步解决桌面文件需要管理员权限才能删除问题
  14. xbrowser连接Linux没有桌面,Windows下使用Xbrowser连接Centos 6.x自带的远程桌面
  15. python实现multi函数参数个数不限、返回所有参数乘积_实现multi( )函数,参数个数不限,返回所有参数的乘积。_学小易找答案...
  16. STM32F1基于正点原子HAL库IIC驱动SH1106芯片的OLED屏
  17. spring中<tx:annotation-driven>标签转为注解@EnableTransactionManagement
  18. 第十五周作业——ZJM与纸条
  19. HDU神、上帝以及老天爷(错排公式)
  20. 一篇文章带你认识GraphQL

热门文章

  1. [生存志] 第54节 武经七书司马法
  2. html设置字体仿宋GB2312,怎么设置仿宋gb2312字体,仿宋gb2312字体设置教程
  3. 计算机网络常用端口号大全
  4. java8进制转换_Java进制转换方法大全_十进制,八进制,二进制,十六进制转换...
  5. MT8127_Android_scatter.txt 文件添加backup分区
  6. 六级核心词汇201~250
  7. QT界面美化与添加多个界面
  8. 基于微信公众号的图书借阅管理系统设计与实现
  9. 未来城市插画mac动态桌面壁纸
  10. STM32F407进入低功耗模式以及唤醒(RTC+中断)