204. 计数质数(素数筛)
给定整数 n
,返回 所有小于非负整数 n
的质数的数量 。
示例 1:
输入:n = 10 输出:4 解释:小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。
示例 2:
输入:n = 0 输出:0
示例 3:
输入:n = 1 输出:0
先上本题做法:(直接用2到sqrt的是过不了的,数据上来就会超时,本题采用欧拉筛,后面会介绍几种求质数的方法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | static bool is_not_p[5000001]; //偷懒写法,因为static在内存中自动用0填充,如果多次运行一定记得初始化为0 vector< int > primes; class Solution { public : void isPrime( int num){ for ( int i=2;i<=num;i++){ if (!is_not_p[i]){ primes.push_back(i); // cout<<i; } for ( int p : primes){ if (p*i>num) break ; is_not_p[p*i]= true ; if (i%p==0) break ; //保证被最小因数筛掉,每个数会且只会被筛一遍 } } } int countPrimes( int n) { int ans=0; isPrime(n); for ( int i=2;i<n;i++){ if (!is_not_p[i]) { // cout<<i<<endl; ans++; } } return ans; } }; |
介绍一下几种素数筛,其中在复杂度上,使用欧拉筛最优:
一、朴素筛法
对于每一个i∈[2,n],枚举[2,i-1] 中是否存在 i 的因子,有=》合数,没有=》素数
又因为对于i而言,因子一定是小于√i的,故枚举可以从 [2,i-1] 优化为 [2,√i],因此复杂度为n√n。
时间复杂度:
O(n√n)
代码如下:
1 2 3 4 5 6 | bool isPrime( int num){ for ( int i=2;i<=( int ) sqrt (num);i++){ if (num%i==0) return false ; } return true ; } |
二、埃氏筛法
首先将2到n范围内的所有整数写下来。其中最小的数字2是素数。将表中所有2的倍数都划去。表中剩余的最小数字是3,他不能被更小的数整除,所以3是素数。再将表中所有3的倍数都划去。如果表中剩余的最小数字是m时,m就是素数。然后将m的倍数都划去。依次类推,反复操作,就能依次枚举出n以内的素数。
时间复杂度:
O(n*log(logn))
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | int prime[MAXN]; //第i个素数 bool is_prime[MAXN+1]; //is_prime[i]为true时表示i为素数 void init(){ int p=0; for ( int i=0;i<n;i++){ is_prime[i]= true ; } is_prime[0]=is_prime[1]= false ; for ( int i=2;i<=n;i++){ if (is_prime[i]){ prime[p++]=i; for ( int j=2*i;j<=n;j+=i){ is_prime[j]= false ; } } } } |
三、欧拉筛法(线性筛法)
欧拉筛,也叫线性筛,可以在 O(n)时间内完成对2~n的筛选。它的核心思想是:让每一个合数被其最小质因数筛到。
欧拉筛法和埃氏筛法的原理类似…但是欧拉筛法更减少了没有必要的计算,就是增加了处理:每一个被筛掉的数都必须是被它的最小质因子筛掉,为了保证这一点,增添了如下代码:
1 2 3 4 | if (i % prime2[k] == 0) //确保是最小质因数 { break ; } |
时间复杂度:
O(n)
完整的算法代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | bool isnp[MAXN]; vector< int > primes; // 质数表 void init( int n){ for ( int i = 2; i <= n; i++){ if (!isnp[i]) primes.push_back(i); for ( int p : primes){ if (p * i > n) break ; isnp[p * i] = 1; if (i % p == 0) break ; } } } |
也可以:
1 2 3 4 5 6 7 8 9 10 11 12 13 | const int N = 1000010; int primes[N], cnt=0; // primes[0]~primes[cnt-1]存储的是0~n中所有的质数(从小到大) bool st[N]; // st[i] == true说明i不是质数 // 线性选法,时间复杂度:O(n) void get_primes( int n) { for ( int i = 2; i <= n; i++) { if (!st[i]) primes[cnt++] = i; for ( int j = 0; primes[j] <= n / i; j++) { st[primes[j] * i] = true ; if (i % primes[j] == 0) break ; } } } |
参考:
https://juejin.cn/post/7085310292237762574
https://juejin.cn/post/7129079647928582175
本文作者:zzzlight
本文链接:https://www.cnblogs.com/zzzlight/p/17623692.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步