[算法] 线性筛(欧拉筛)

我搞了一个下午和一个晚上,网上的博客、视频讲得不清不楚,只会讲模拟却讲不出所以然,感觉真的很难!!!下面是自己的理解,不保证没问题!

以下代码按照该题来写:模板题204. 计数质数

简述:埃氏筛算法中,同一个合数会被多个质数标记(例如 45 这个数,它会同时被 3,5 两个数标记为合数),线性筛则保证每个合数只会被其最小质数因数标记。很难搞懂,如果实在搞不懂,没必要花精力,因为复杂度和埃氏筛差别不大。

时间复杂度O(n)

空间复杂度O(n)

示例代码(C++)

class Solution { public: int countPrimes(int n) { vector<bool> isNotprimes(n); // 标记是否为合数 vector<int> primes; // 存遍历到的质数,使用vector会很慢 int ans = 0; for (int i = 2; i < n; ++i) { if (!isNotprimes[i]) { // 质数 ++ans; primes.push_back(i); // 添加到质数数组 } /** * 标记i的倍数为合数(筛掉):“j < primes.size()”条件可以省略,因为: * - 如果i是质数,那么if语句的条件一定在primes最后一个元素成立, * - 如果i是合数,那么一定会小于它的(已在primes中的)质数整除 */ for (int j = 0; (long long)i * primes[j] < n; ++j) { isNotprimes[i * primes[j]] = true; // 如果i为合数,且 “i的最小质因子*i” 未越界,则 “i的最小质因子*i” 得到的合数一定能被筛掉 /** * - 此if条件判断的作用:primes数组中的质数是递增的,当 “i%primes[j]==0” 成立(i能被primes[j]整除), * 那么 “i*primes[j+1]” 这个合数肯定也可以被 “primes[j]” 筛掉, * 因为i中的含有 “primes[j]” ,也就是 “i*primes[j+1]=(k*primes[j])*primes[j+1]”(其中k为i的另一个因子), * 所以,当 “i%primes[j]==0” 成立时,需要终止,否则当i遍历到 “k*primes[j+1]” 时, * “i*primes[j+1]” 也就是 “(k*primes[j])*primes[j+1]” 会再次被筛掉。 */ /** * - 那当前遍历的i如果是合数,会不会在之前没被筛掉? * 答:不会,假设一个合数为z,它的最小质因子为x,z=x*y(其中,y为另一个因子,它可能为质数也可能为合数),必有x<=y, * 由于x>1,因此有 y<z,当i遍历到y,分两种情况: * (1)当y质数,那么 “i%primes[j]==0” 的成立条件为 “primes[j]=y”,因此x必被primes[j]遍历到,也就是z必被筛掉。 * (2)当y为合数,那么此时y可类比成当前的z,也可分解成两个数 y=x_y*y_y(下划线后面的y表示是y的因子,x_y为y的最小质因子), * 由于任何合数都能被分解成多个质因子相乘,y即是合数也是z的因子,因此y的因子也是z的因子, * 由于x是z的最小质因子,因此必有x<=x_y。 * 由于对于一个合数y,只要 “y*x_y<n” ,也就是循环的条件成立(不越界),那么 x_y 必被primes[j]遍历到, * 而 x<x_y,primes数组又是从小到大遍历,因此 x 必被primes[j]遍历到,也就是z必被筛掉。 */ if (i % primes[j] == 0) { // 整除中断,保证合数被最小的质因数筛掉 break; } } } return ans; } };

C版本

class Solution { public: int countPrimes(int n) { bool isNotprimes[5000005]; int primes[5000005]; int count = 0; // primes质数的个数 int ans = 0; for (int i = 2; i < n; ++i) { if (!isNotprimes[i]) { ++ans; primes[count++] = i; } for (int j = 0; (long long)i * primes[j] < n; ++j) { isNotprimes[i * primes[j]] = true; if (i % primes[j] == 0) { break; } } } return ans; } };

  1. 线性筛理论上比埃氏筛快,但实际上差不了多少(一个是O(n),一个是O(nloglogn)),但是如果代码中的数组使用了vector,由于线性筛算法涉及到多次数组存取(vector访问速度比原生数组慢)和内存的动态分配,可能会比埃氏筛慢不少。 ↩︎

posted @   小贼的自由  阅读(336)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示