深入解析素数筛法:从埃氏筛到欧拉筛的算法思想与实现
素数筛法是一种用于高效生成素数的算法。常见的素数筛法包括埃拉托斯特尼筛法(埃氏筛)和欧拉筛(线性筛)。下面我们将详细讲解这两种筛法的思想:
一、 埃拉托斯特尼筛法(埃氏筛)
思想:
埃氏筛的基本思想是从2开始,将每个素数的倍数标记为合数,直到筛完所有小于等于给定范围的数。具体步骤如下:
-
初始化一个布尔数组
isPrime[],大小为n+1,并将所有元素初始化为true。 -
从2开始遍历到
sqrt(n),如果当前数i是素数(即isPrime[i]为true),则将其所有倍数标记为合数(即isPrime[j]设为false)。 -
最后,所有
isPrime[i]为true的i即为素数。
时间复杂度:
埃氏筛的时间复杂度为 O(n log log n)。
未优化代码实现:
const int N = 1e7; // 定义空间大小,1e7 约 10MB
int prime[N+1]; // 存放素数,记录 visit[i] = false 的项
bool visit[N+1]; // visit[i] = true 表示 i 被筛掉,不是素数int E_sieve(int n) { // 埃氏筛法,计算 [2,n] 内的素数int k = 0; // 统计素数的个数for(int i = 0; i <= n; i++) visit[i] = false; // 初始化for(int i = 2; i <= n; i++) { // 从第一个素数 2 开始if(!visit[i]) { // 如果 i 是素数prime[++k] = i; // 将 i 存入 prime 数组for(int j = 2*i; j <= n; j += i) // 筛掉 i 的倍数visit[j] = true; // 标记为非素数}}return k; // 返回素数的个数
}
上述代码有两处可以优化:
(1)用来做筛选的数2、3、5等,最多到sqrt(n)就可以了。例如,求n=100以内的素数,用2、3、5、7筛选就足够了。其原理和试除法一样;非素数k,必定可以被一个小于或等于sqrt(k)的素数整除,被筛掉。这个优化很大,缩短了时间复杂度。
(2)for(int j = 2*i; j <= n; j += i)中的j=2*i优化为j=i*i。例如i=5,2*5,3*5,4*5已经在前面i=2,3,4的时候筛过了。这个优化较小,形如一个正方形的数组矩阵被砍成一个三角形。
优化代码实现:
int E_sieve(int n) {for(int i = 0; i <= n; i++) visit[i] = false; // 初始化for(int i = 2; i <= sqrt(n); i++) // 只需筛到 sqrt(n)if(!visit[i]) // 如果 i 是素数for(int j = i * i; j <= n; j += i) // 从 i^2 开始筛visit[j] = true; // 标记为非素数int k = 0;for(int i = 2; i <= n; i++)if(!visit[i]) prime[++k] = i; // 存素数return k; // 返回素数的个数
}
埃氏筛的计算复杂度:
2的倍数被筛掉,计算n/2次;3的倍数被掉,计算n/3次;5的倍数被筛掉,计算n/5次;……;总计算量等于n/2+n/3+n/5+n/7+n/11+…,约为O(n log log n)。其计算量很接近线性的(n),已经相当好了。
空间复杂度:
代码用到了bool visit[N+1]数组,当N=10^7时约10MB。由于埃氏筛只能用于处理约n=10^7的问题,10MB空间是够用的。
埃氏筛可以计算出[2,n]内的素数,不过更常见的应用场景是计算[L,R]区间内的素数,L、R极大,但R一L较小,此时也可以用埃氏筛。
最终C++代码实现:
#include <iostream> // 引入输入输出流库,用于标准输入输出(如cout)
#include <vector> // 引入向量库,用于动态数组的实现
#include <cmath> // 引入数学库,用于数学函数(如sqrt)// 定义埃拉托斯特尼筛法函数,参数n表示筛选素数的范围
void sieveOfEratosthenes(int n) {// 创建一个大小为n+1的布尔向量isPrime,初始值为true,表示所有数默认是素数std::vector<bool> isPrime(n + 1, true);// 0和1不是素数,手动设置为falseisPrime[0] = isPrime[1] = false;// 从2开始遍历到sqrt(n),因为大于sqrt(n)的数的倍数已经被更小的素数标记过了for (int i = 2; i <= std::sqrt(n); ++i) {// 如果当前数i是素数(isPrime[i]为true)if (isPrime[i]) {// 从i的平方开始,将i的所有倍数标记为合数(false)// 因为小于i的倍数已经被更小的素数标记过了for (int j = i * i; j <= n; j += i) {isPrime[j] = false;}}}// 输出所有素数for (int i = 2; i <= n; ++i) {// 如果isPrime[i]为true,说明i是素数,输出iif (isPrime[i]) {std::cout << i << " ";}}// 输出换行符,使结果更美观std::cout << std::endl;
}// 主函数
int main() {int n = 50; // 定义筛选范围的上限为50sieveOfEratosthenes(n); // 调用埃拉托斯特尼筛法函数return 0; // 程序正常结束
}
二、 欧拉筛(线性筛)
思想:
欧拉筛是一种改进的筛法,能够在 O(n) 的时间复杂度内筛出所有素数。其核心思想是让每个合数只被其最小的质因数筛掉,从而避免重复标记。具体步骤如下:
-
初始化一个布尔数组
isPrime[],大小为n+1,并将所有元素初始化为true。 -
初始化一个数组
primes[]用于存储素数。 -
从2开始遍历到
n,如果当前数i是素数,则将其加入primes[]。 -
对于每个素数
primes[j],如果i * primes[j]超过n则停止;否则将isPrime[i * primes[j]]设为false。如果i能被primes[j]整除,则停止内层循环。
时间复杂度:
欧拉筛的时间复杂度为 O(n)。
C++实现:
#include <iostream>
#include <vector>void eulerSieve(int n) {std::vector<bool> isPrime(n + 1, true);std::vector<int> primes;for (int i = 2; i <= n; ++i) {if (isPrime[i]) {primes.push_back(i);}for (int j = 0; j < primes.size() && i * primes[j] <= n; ++j) {isPrime[i * primes[j]] = false;if (i % primes[j] == 0) {break;}}}// 输出所有素数for (int prime : primes) {std::cout << prime << " ";}std::cout << std::endl;
}int main() {int n = 50;eulerSieve(n);return 0;
}
总结:
-
埃氏筛:简单易懂,时间复杂度为
O(n log log n),适合用于较小的范围。 -
欧拉筛:时间复杂度为
O(n),适合用于较大的范围,且避免了重复标记。
三、 例题讲解
P1835 素数密度 - 洛谷

算法代码:
#include<bits/stdc++.h> // 包含标准库中的所有头文件
using namespace std; // 使用标准命名空间
const int N=1e6+1; // 定义常量 N,表示数组的最大大小
int prime[50000]; // 用于存储素数的数组
bool vis[N+1]; // 用于标记是否为素数的布尔数组int E_sieve(int n) // 埃氏筛法函数,用于找出小于等于 n 的所有素数
{for(int i=0;i<=n;i++) // 初始化 vis 数组{vis[i]=false; // 将所有元素初始化为 false,表示初始时都认为是素数}for(int i=2;i<=sqrt(n);i++) // 从 2 开始筛除非素数{if(!vis[i]) // 如果 i 是素数{for(int j=i*i;j<=n;j+=i) // 筛除 i 的所有倍数{vis[j]=true; // 标记为非素数}}}int k=0; // 用于统计素数的个数for(int i=2;i<=n;i++) // 遍历所有数{if(!vis[i]) // 如果 i 是素数{prime[++k]=i; // 将素数存入 prime 数组}}return k; // 返回素数的个数
}int main() // 主函数
{int cnt=E_sieve(50000); // 调用埃氏筛法函数,找出小于等于 50000 的所有素数,并返回素数的个数int L,R; // 定义区间 [L, R]cin>>L>>R; // 输入区间 [L, R]if(L==1) // 如果 L 为 1,将其调整为 2,因为 1 不是素数{L=2;}memset(vis,0,sizeof(vis)); // 初始化 vis 数组为 0for(int i=1;i<=cnt;i++) // 遍历所有素数{int p=prime[i]; // 获取当前素数long long start; // 定义筛除的起始位置if((L+R-1)/p*p>2*p) // 计算起始位置{start=(L+p-1)/p*p;}else{start=2*p;}for(long long j=start;j<=R;j+=p) // 筛除当前素数的倍数{vis[j-L+1]=true; // 标记为非素数}}int ans=0; // 用于统计区间 [L, R] 内的素数个数for(int i=1;i<=R-L+1;++i) // 遍历区间 [L, R]{if(!vis[i]) // 如果当前数是素数{ans++; // 素数个数加 1}}cout<<ans; // 输出区间 [L, R] 内的素数个数
}
代码思路
这段代码的主要功能是使用埃氏筛法找出给定区间 [L, R] 内的所有素数,并输出该区间内素数的个数。以下是代码的思路和实现步骤:
1. 包含头文件和定义常量
-
包含标准库头文件
<bits/stdc++.h>,以便使用所有标准库函数。 -
定义常量
N表示数组的最大大小,prime数组用于存储素数,vis数组用于标记是否为素数。
2. 埃氏筛法函数 E_sieve
-
功能:找出小于等于
n的所有素数。 -
实现步骤:
-
初始化
vis数组,将所有元素标记为false,表示初始时所有数都被认为是素数。 -
从 2 开始遍历到
sqrt(n),筛除非素数:-
如果当前数
i是素数(vis[i] == false),则筛除i的所有倍数(从i*i开始,步长为i)。 -
将筛除的数标记为
true,表示它们是非素数。
-
-
遍历所有数,将未被筛除的数(即素数)存入
prime数组。 -
返回素数的个数。
-
3. 主函数 main
-
功能:计算区间
[L, R]内的素数个数。 -
实现步骤:
-
调用
E_sieve函数,找出小于等于 50000 的所有素数,并返回素数的个数cnt。 -
输入区间
[L, R]。 -
如果
L == 1,将其调整为 2,因为 1 不是素数。 -
初始化
vis数组为 0,用于标记区间[L, R]内的数是否为素数。 -
遍历所有素数
prime[i]:-
计算当前素数
p在区间[L, R]内的起始筛除位置start。 -
从
start开始,筛除p的所有倍数,并将这些数标记为非素数。
-
-
统计区间
[L, R]内未被标记的数(即素数)的个数ans。 -
输出
ans。
-
4. 关键点
-
埃氏筛法:用于高效找出小于等于
n的所有素数。 -
区间筛法:利用埃氏筛法找出的素数,筛除区间
[L, R]内的非素数。 -
起始位置计算:
-
对于每个素数
p,筛除的起始位置为max(p * 2, (L + p - 1) / p * p)。 -
这样可以避免重复筛除已经在之前步骤中处理过的数。
-
5. 代码优化
-
空间优化:使用
vis数组标记区间[L, R]内的数是否为素数,而不是整个范围[1, N]。 -
时间优化:只筛除区间
[L, R]内的数,避免不必要的计算。
代码流程图
-
初始化:
-
定义常量、数组和变量。
-
调用
E_sieve函数,找出小于等于 50000 的所有素数。
-
-
输入区间
[L, R]:-
如果
L == 1,调整为L = 2。
-
-
筛除区间
[L, R]内的非素数:-
遍历所有素数
prime[i]。 -
计算起始位置
start。 -
筛除
p的所有倍数,并标记为非素数。
-
-
统计素数个数:
-
遍历区间
[L, R],统计未被标记的数的个数。
-
-
输出结果:
-
输出区间
[L, R]内的素数个数。
-
定位的数学思路:(难想咯)


相关文章:
深入解析素数筛法:从埃氏筛到欧拉筛的算法思想与实现
素数筛法是一种用于高效生成素数的算法。常见的素数筛法包括埃拉托斯特尼筛法(埃氏筛)和欧拉筛(线性筛)。下面我们将详细讲解这两种筛法的思想: 一、 埃拉托斯特尼筛法(埃氏筛) 思想࿱…...
关于前端指令
在前端开发中,指令(Directives)通常指在框架中使用的一种特殊的语法或机制,用于扩展 HTML 的功能。常见的指令主要存在于前端框架中,如 Vue.js、Angular 等。下面我们将分别介绍 Vue.js 和 Angular 中的常用指令&#…...
ubuntu20.04系统没有WiFi图标解决方案_安装Intel网卡驱动
文章目录 1. wifi网卡配置1.1 安装intel官方网卡驱动backport1.1.1 第四步可能会出现问题 1.2 ubuntu官方的驱动1.3 重启 1. wifi网卡配置 我的电脑是华硕天选4(i7,4060),网卡型号intel ax201 ax211 ax210通用。 参考文章&#…...
蓝桥杯day2:解码异或 后的数组
一、题意 未知 整数数组 arr 由 n 个非负整数组成。 经编码后变为长度为 n - 1 的另一个整数数组 encoded ,其中 encoded[i] arr[i] XOR arr[i 1] 。例如,arr [1,0,2,1] 经编码后得到 encoded [1,2,3] 。 给你编码后的数组 encoded 和原数组 arr …...
Vite+微前端Qiankun-状态管理
一、前言 在微前端架构中,状态管理是一个重要的课题。由于子应用是独立的,它们之间可能需要共享状态或通信。以下是基于qiankun微前端架构的状态管理方案,结合Vue 3和Vite的实现。 二、状态管理方案 在微前端中,状态管理可以分为…...
【初学者】Python语言中有没有指针类型?
李升伟 整理 在Python语言中,没有像C或C那样的显式指针类型。Python的设计哲学强调简洁和易读,因此它隐藏了许多底层的细节,包括指针。 不过,Python中的变量可以被视为对对象的引用。当你创建一个对象并将其赋值给一个变量时&am…...
网络编程---多客户端服务器
写一个服务器和两个客户端 运行服务器和2个客户端,实现聊天功能 客户端1 和 客户端2 进行聊天 客户端1将聊天数据发送给服务器 服务器将聊天数据转发给客户端2 要求: 服务器使用 select 模型实现 客户端1使用 poll 模型实现 客户端2使用 多线程实现…...
SPACE_GAME
以下是一些關於星際遊戲的 GitHub 代碼範本,您可以根據需求進行修改或擴展。這裡提供一個簡單的 Python 代碼範例,展示如何創建一個簡單的星際遊戲框架。 專案結構 space_game/ ├── main.py ├── spaceship.py ├── enemy.py └── README.md1…...
Web Component 教程(五):从 Lit-html 到 LitElement,简化组件开发
前言 在现代前端开发中,Web 组件是一种非常流行的技术,它允许我们创建可重用的、自包含的 UI 元素。而 Lit-html 是一个简洁高效库,用于在 Web 组件中进行渲染。在这篇教程中,我们一步步学习如何 Lit-html 来创建 Web Component。…...
Vue3:构建高效用户界面的利器
一、Vue.js 简介 Vue.js(读音 /vjuː/, 类似于 view)是一套构建用户界面的渐进式框架。它只关注视图层,采用自底向上增量开发的设计。Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件 ,学习起来非常简单…...
LeetCode 2614.对角线上的质数:遍历(质数判断)
【LetMeFly】2614.对角线上的质数:遍历(质数判断) 力扣题目链接:https://leetcode.cn/problems/prime-in-diagonal/ 给你一个下标从 0 开始的二维整数数组 nums 。 返回位于 nums 至少一条 对角线 上的最大 质数 。如果任一对角线上均不存在质数&…...
红日靶场(二)——个人笔记
靶场搭建 新增VMnet2网卡 **web:**需要配置两张网卡,分别是外网出访NAT模式和内网域环境仅主机模式下的VMnet2网卡。 **PC:**跟web一样,也是需要配置两张网卡,分别是外网出访NAT模式和内网域环境仅主机模式下的VMn…...
实时视频分析的破局之道:蓝耘 MaaS 如何与海螺 AI 视频实现高效协同
一、蓝耘 MaaS 平台:AI 模型全生命周期管理的智能引擎 蓝耘 MaaS(Model-as-a-Service)平台是由蓝耘科技推出的 AI 模型全生命周期管理平台,专注于为企业和开发者提供从模型训练、推理到部署的一站式解决方案。依托云原生架构、高…...
清晰易懂的 Swift 安装与配置教程
初学者也能看懂的 Swift 安装与配置教程 本教程将手把手教你如何在 macOS 系统上安装 Swift,配置依赖包缓存位置,并指出新手容易踩坑的细节。即使你是零基础小白,也能快速上手! 一、安装 Swift(macOS 环境)…...
大数据 ETL 异常值缺失值处理完整方案
在大数据时代,数据已成为推动业务创新与决策优化的重要资产。然而,数据的海量、异构及实时性往往伴随着噪声、错误记录以及缺失现象,严重影响下游分析模型的准确性和可靠性。尤其在 ETL(抽取、转换、加载)环节中,如何在海量数据流中迅速甄别并处理异常数据,便成为决定整…...
macOS homebrew - 切换源
https://mirrors.tuna.tsinghua.edu.cn/help/homebrew/ 环境变量中 添加: export HOMEBREW_BREW_GIT_REMOTE"https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git" export HOMEBREW_CORE_GIT_REMOTE"https://mirrors.tuna.tsinghua.edu.cn…...
如何基于Gone编写一个Goner对接Apollo配置中心(下)—— 对组件进行单元测试
项目地址:https://github.com/gone-io/gone 原文地址:https://github.com/gone-io/goner/blob/main/docs/test_goner.md 本文介绍的例子,代码在:https://github.com/gone-io/goner/blob/main/apollo 文章目录 引言编写“可测试”的…...
走进Java:String字符串的基本使用
❀❀❀ 大佬求个关注吧~祝您开心每一天 ❀❀❀ 目录 一、什么是String 二、如何定义一个String 1. 用双引号定义 2. 通过构造函数定义 三、String中的一些常用方法 1 字符串比较 1.1 字符串使用 1.2 字符串使用equals() 1.3 使用 equalsIgnoreCase() 1.4 cpmpareTo…...
python系列之元组(Tuple)
不为失败找理由,只为成功找方法。所有的不甘,因为还心存梦想,所以在你放弃之前,好好拼一把,只怕心老,不怕路长。 python系列之元组(Turple) 一、元组是什么?——给新手的…...
破解验证码新利器:基于百度OCR与captcha-killer-modified插件的免费调用教程
破解验证码新利器:基于百度OCR与captcha-killer-modified插件的免费调用教程 引言 免责声明: 本文提供的信息仅供参考,不承担因操作产生的任何损失。读者需自行判断内容适用性,并遵守法律法规。作者不鼓励非法行为,保…...
批量删除 PPT 中的所有图片、某张指定图片或者所有二维码图片
PPT 文档中的图片如何删除呢?相信很多小伙伴或碰到类似的需求。比如我们需要删除 PPT 文档中的某一张图片或者某张二维码图片,如果每一页都有这张图片,或者有很多 ppt 都有同一张要删除的图片,我们应该怎么快速的完成删除呢&#…...
大模型开发(六):LoRA项目——新媒体评论智能分类与信息抽取系统
LoRA项目——新媒体评论智能分类与信息抽取系统 0 前言1 项目介绍1.1 项目功能1.2 技术原理1.3 软硬件环境1.4 项目结构 2 数据介绍与处理2.1 数据集介绍2.2 数据处理2.3 数据导入器 3 模型训练3.1 配置文件3.2 工具函数3.3 模型训练3.4 模型评估 4 模型推理 0 前言 微调里面&…...
mysql-innodb存储引擎主键索引叶子结点数据结构(非单纯的双向链表)
我们应该清楚行记录是放在页中的。 compact行记录格式: 主要介绍几个比较重要的参数 heap_no: 页号 record_type: 0 表示普通类型(叶子结点),1表示B树的非叶子节点 ,2 表示最小记录ÿ…...
MySQL 进阶学习文档
一、存储引擎 1.1 核心架构 四层架构:连接层 → 服务层 → 引擎层 → 存储层插件式存储引擎:不同引擎独立管理数据存储,可动态选择 1.2 主流引擎对比 特性InnoDB(默认)MyISAMMemory事务支持✅ 支持❌ 不支持❌ 不支…...
物联网为什么用MQTT不用 HTTP 或 UDP?
先来两个代码对比,上传温度数据给服务器。 MQTT代码示例 // MQTT 客户端连接到 MQTT 服务器 mqttClient.connect("mqtt://broker.server.com:8883", clientId) // 订阅特定主题 mqttClient.subscribe("sensor/data", qos1) // …...
Vmware中的centos7连接上网
有很多刚刚开始配置了centos7,然后发现不能上网现在来解决这个问题。 测试能不能上网 先还原这个设置,如果没有动过的话就不用,连接模式是NAT模式 然后进去设置网络环境,记得是用超级用户设置 vi /etc/sysconfig/network-script…...
【AI知识】常见的优化器及其原理:梯度下降、动量梯度下降、AdaGrad、RMSProp、Adam、AdamW
常见的优化器 梯度下降(Gradient Descent, GD)局部最小值、全局最小值和鞍点凸函数和非凸函数动量梯度下降(Momentum)自适应学习率优化器AdaGrad(Adaptive Gradient Algorithm)RMSProp(Root M…...
线性规划的标准形式
标准形式的定义 目标函数:最大化线性目标函数 其中,x 是决策变量向量,c 是目标系数向量。 约束条件:等式形式约束 A x b, 其中,A 是约束系数矩阵,b 是常数项向量。 变量非负约束: 。 因此…...
网络安全应急入门到实战
奇安信:95015网络安全应急响应分析报告(2022-2024年)官网可以下载 https://github.com/Bypass007/Emergency-Response-Notes 应急响应实战笔记 网络安全应急响应技术实战指南 .pdf 常见场景 第4章 勒索病毒网络安全应急响应 第5章 挖矿木…...
应用程序安全趋势:左移安全、人工智能和开源恶意软件
软件是大多数行业业务运营的核心,这意味着应用程序安全从未如此重要。 随着组织采用云原生架构、微服务和开源组件,攻击面不断扩大。结果是:攻击者渴望利用的易受攻击和恶意依赖项数量不断增加。 2025 年,安全团队将面临日益复杂…...
