数论之欧拉筛法(含朴素筛选、埃式筛选详细代码)

news2024/11/15 10:50:30

文章目录

  • 前言
  • 朴素筛法(纯暴力,O(n^2^))
  • 埃式筛法(找出合数来确认质数, O(n*log(logn)))
  • 欧拉筛法(线性筛选,O(n))
  • 参考文章

前言

在学习Acwing c++蓝桥杯辅导课第八讲数论-1295. X的因子链时接触到了欧拉筛法,本章就从最原始的朴素筛法来开始到最终的欧拉筛法。

本章节的测试数据n为100万,问题围绕从去找寻指定[2, n]范围找到所有质数个数开始

当前文章已收录到博客文件目录索引:博客目录索引(持续更新)


朴素筛法(纯暴力,O(n2))

思路:每判断一个n是否是质数,就去遍历[2, n - 1]来去看是否能够整除,若是都不能够整除表示其是一个质数。

复杂度分析:时间复杂度O(n2)

class Main {

    //朴素筛法:O(n^2^)
    public static boolean isPrime(int n) {
        for (int i = 2; i < n; i++) {
            if (n % i == 0) return false;
        }
        return true;
    }

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        //int N = (int) 1e6;
        int N = 100;
        int count = 0;
        for (int i = 2; i <= N; i++) {
            if (isPrime(i)) count++;
        }
        System.out.println(count);
        long endTime = System.currentTimeMillis();
        System.out.println("执行时间:" + (float) (endTime - startTime) / 1000 + "s");
    }
}

进一步来进行优化,实际上对于isPrime中遍历到的终点n实际上可以去进行开根号:

//朴素筛法优化:O(n^2^)
public static boolean isPrime(int n) {
    //开根号
    int sqt = (int) Math.sqrt(n);
    for (int i = 2; i <= sqt + 1; i++) {
        if (n % i == 0) return false;
    }
    return true;
}

image-20230120134657481

其他写法:

for (int i = 2; i <= n / i; i++)
   
//不推荐,i*i可能会出现溢出情况,建议通过上面的n / i来进行不会出现溢出
for (int i = 2; i * i <= n ; i++)

埃式筛法(找出合数来确认质数, O(n*log(logn)))

合数:合数是指在大于1的整数中除了能被1和本身整除外,还能被其他数(0除外)整除的数。

思路:首先去使用数组来存储所有的合数,接着来去判断对应的数字是否是质数。

时间复杂度:O(n*log(logn))

package com.changlu.java;

class Main {

    static int N = (int) 1e6;
    //筛选出所有的合数(为1的,合数:是指在大于1的整数中除了能被1和本身整除外,还能被其他数(0除外)整除的数。)
    static int[] coms = new int[N + 1];

    //埃式筛法
    public static void ai(int n) {
        int sqt = (int) Math.sqrt(n);
        //这里我们来缩减范围到根号开始去找寻所有的合数
        for (int i = 2; i <= sqt + 1; i ++ ) {
            //若是当前是质数,则找出该质数的倍乘合数
            if (coms[i] == 0) {
                //最大范围应当我们要找的合数值
                for (int j = i * i; j <= n; j += i) coms[j] = 1;
            }
        }
    }

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        int count = 0;
        //进行埃式筛选
        ai(N);
        //判断质数
        for (int i = 2; i <= N; i++) {
            //为1的表示合数,对应0即为质数
            if (coms[i] == 0) count++;
        }
        System.out.println(count);
        long endTime = System.currentTimeMillis();
        System.out.println("执行时间:" + (float) (endTime - startTime) / 1000 + "s");
    }
}

image-20230120142200877


欧拉筛法(线性筛选,O(n))

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

实际上在埃式筛法中找寻合数时,对于某个数会有多次重复找到例如:

i = 2时,会找出4 7 8 16 ... n
i = 3时,则会找到6 9 12

如何来解决这种多次重复找的情况呢?此时就可以使用欧拉筛法,实际上就是埃式筛法的升级版。

在欧拉筛法中我们添加一个primes数组来记录质数,对于倍乘是针对于这个primes来围绕找新的质数的,并且一旦当前数能够整除primes数组中的值时,那么找合数环节就直接结束了!虽然说是两重循环,但是它的时间复杂度是线性的。

代码

复杂度分析:时间复杂度O(n)

package com.changlu.java;

class Main {

    static int N = (int) 1e6;
    //筛选出所有的合数(为1的,合数:是指在大于1的整数中除了能被1和本身整除外,还能被其他数(0除外)整除的数。)
    static int[] coms = new int[N + 1];
    //记录所有的质数
    static int[] primes = new int[N + 1];
    //统计质数的个数
    static int ps_pos = 0;

    //欧拉筛:可筛选出所有质数以及每个数最小的质因数
    //时间复杂度O(n)
    public static void ola(int n ) {
        //遍历所有的数情况
        for (int i = 2; i <= n; i ++ ) {
            //记录质数
            if (coms[i] == 0) primes[ps_pos++] = i;
            //依据现有的质数来去求得倍乘质数(解决埃拉筛选中重复的问题)
            //遍历质数数组primes所有项,来进行求得对应当前数i能够构成的质数
            for (int j = 0; primes[j] * i <= n && j < ps_pos; j++) {
                //设置合数
                coms[primes[j] * i] = 1;
                //如果说当前的i能够整除拥有的质数,那么直接结束(核心)
                if (i % primes[j] == 0) break;
            }
        }
    }

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        int count = 0;
        //进行欧拉筛选
        ola(N);
        //遍历质数的个数
        System.out.println(ps_pos);
        long endTime = System.currentTimeMillis();
        System.out.println("执行时间:" + (double) (endTime - startTime) / 1000 + "s");
    }
}

image-20230120151237085


参考文章

[1]. 【C++算法】20分钟学会高效地素数筛法,埃氏筛法,欧拉筛法:视频讲解

[2]. 欧拉筛法(线性筛)的学习理解

[3]. 线性筛(欧拉筛)——算法解析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/174006.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux常用命令——tcpdump命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) tcpdump 一款sniffer工具&#xff0c;是Linux上的抓包工具&#xff0c;嗅探器。 补充说明 tcpdump命令是一款抓包&#xff0c;嗅探器工具&#xff0c;它可以打印所有经过网络接口的数据包的头信息&#xff0c;…

【MySQL】CentOS7 卸载以及安装 MySQL 详细流程

一、卸载 MySQL 查看 MySQL 安装版本 mysqladmin --version通过 rpm 查找 MySQL rpm -qa|grep -i mysql查看 MySQL 运行状态 systemctl status mysqld.service关闭 MySQL 服务 systemctl stop mysqld.service通过 yum remove 删除 MySQL 安装包 把上面所有的安装包挨个删除…

用友U8和旺店通·企业奇门单据接口对接

对接系统旺店通企业奇门旺店通是北京掌上先机网络科技有限公司旗下品牌&#xff0c;国内的零售云服务提供商&#xff0c;基于云计算SaaS服务模式&#xff0c;以体系化解决方案&#xff0c;助力零售企业数字化智能化管理升级。为零售电商企业的订单管理及仓储管理提供解决方案&a…

Java寒假作业——编程题

二、编程题&#xff08;ACM模式&#xff09;1-2题1 &#xff08;2022蚂蚁金服&#xff09;一个字母可以拆分成两个字母表顺序的前一个字母&#xff0c;例如&#xff0c;b可以拆分成aa&#xff0c;c可以拆分成bb。打印出最短的可以拆分成 K 个 a 的字符串&#xff0c;字母顺序无…

Spring的三种装配机制(XML、JavaConfig、自动装配)

Spring中bean有三种装配机制一、在xml中显示装配二、 在java中显式装配&#xff0c;都需要在Config配置类重写三、自动装配1. xml方式自动装配2. 使用注解装配等于依赖关系注入&#xff0c;即一个Bean作为属性&#xff0c;装配到另一个Bean。 一、在xml中显示装配 <!--1. …

GCC是什么

GCC是什么 说到 GCC&#xff0c;就不得不提 GNU&#xff0c;“GNU”是“GNUs Not Unix!”&#xff08;GNU并非Unix&#xff01;&#xff09;的首字母递归缩写&#xff0c;中文名“革奴计划”。GNU 计划的最终目标是打造出一套完全自由&#xff08;即自由使用、自由更改、自由发…

Java基础之《netty(28)—TCP粘包拆包原理》

一、基本介绍 1、TCP是面向连接的&#xff0c;面向流的&#xff0c;提供高可靠性服务。收发两端&#xff08;客户端和服务器端&#xff09;都要有一一成对的socket&#xff0c;因此&#xff0c;发送端为了将多个发给接收端的包&#xff0c;更有效的发给对方&#xff0c;使用了…

C++语法复习笔记-第6章 c++指针

文章目录1. 计算机内存1. 储存层次2. 内存单元与地址3. 指针定义2. 左值与右值1. 数组与指针1. 概念3. C中的原始指针1. 数组指针与指针数组2. const pointer 与 pointer to const3. 指向指针的指针4.关于野指针4.1 指向指针的指针4.2 NULL指针4.3 野指针5. 指针的基本运算5.1 …

MySQL 批量插入

文章目录MySQL批量插入10w条数据创建表创建函数创建存储过程调用存储过程MySQL批量插入10w条数据 创建表 创建emp&#xff08;部门&#xff09;表 创建dept&#xff08;员工&#xff09;表 创建函数 创建rand_num函数&#xff0c;随机生成部门编号&#xff0c;保证部门编…

Spark RDD算子

文章目录Spark RDD算子一、RDD 转换算子1、Value 类型(1) mapSpark RDD算子 RDD 方法也叫做RDD算子&#xff0c;主要分为两类&#xff0c;第一类是用来做转换的&#xff0c;例如flatMap()&#xff0c;Map()方法&#xff0c;第二类是行动的&#xff0c;例如&#xff1a;collenc…

Spring Security in Action 第八章 配置授权:api授权

本专栏将从基础开始&#xff0c;循序渐进&#xff0c;以实战为线索&#xff0c;逐步深入SpringSecurity相关知识相关知识&#xff0c;打造完整的SpringSecurity学习步骤&#xff0c;提升工程化编码能力和思维能力&#xff0c;写出高质量代码。希望大家都能够从中有所收获&#…

并查集(Java实现)

基本实现 任务&#xff1a; 维护多个不相交的集合&#xff0c;支持两种操作&#xff1a;合并两个集合&#xff0c;查询一个元素所在的集合。 说明&#xff1a; 维护一个森林&#xff0c;每一棵树都代表一个集合&#xff0c;树根元素为这个集合的代表元。利用数组father[]查询记…

[标准库]STM32F103R8T6 串口的收发

前言 这篇记录一下怎么调用标准库的函数来初始化一个串口&#xff0c;并调库实现发数据和收数据&#xff0c;以及串口收中断的使用。 越往深处学习越感觉其实32就是一个功能更加齐全和强大的MCU&#xff0c;其实跟51没有什么本质上的区别。很多设置的地方都是同质化的。比如需…

JVM知识点整理(整理中)

JVM知识点整理1、JVM与java体系结构1.1、java的体系结构1.2、JVM1.2.1、从跨平台的语言到跨语言的平台1.2.2、常用的JVM实现1.2.3、JVM的位置1.2.4、JDK、JER、JDK1.2.5、JVM的整体结构1.2.6、java代码的执行流程1.2.7、JVM的代码模型1.2.8、JVM的生命周期2、类加载子系统2.1、…

ARM NandFlash 介绍

一、NandFlash 的接口 1、Nand 的型号与命名 (1) Nand 的型号命名都有含义&#xff0c;就拿 K9F2G08 来示例分析一下&#xff1a;K9F 表示是三星公司的 NandFlash 系列。2G 表示 Nand 的大小是 2Gbit&#xff08;256MB&#xff09;。08 表示 Nand 是 8 位的&#xff08; 8 位…

员工入职管理系统|员工管理系统|基于SpringBoot+Vue的企业新员工入职系统

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助 收藏点赞不迷路 关注作者有好处 文末获取源…

SICTF2023 WP

前言 新年前的最后一场比赛&#xff0c;感谢shenghuo2师傅提供的misc和密码的wp&#xff0c;把misc和密码ak了&#xff0c;太强了 web 兔年大吉 源码 <?php highlight_file(__FILE__); error_reporting(0);class Happy{private $cmd;private $content;public function _…

Registration Center

CAP●一致性(Consistency)&#xff1a;所有节点在同一时间具有相同的数据&#xff1b;●可用性(Availability) &#xff1a;保证每个请求不管成功或者失败都有响应&#xff1b;某个系统的某个节点挂了&#xff0c;但是并不影响系统的接受或者发出请求。●分隔容忍(Partition to…

python循环语句

Python循环语句 文章目录Python循环语句一、实验目的二、实验原理三、实验环境四、实验内容五、实验步骤1.While循环结构2.While无限循环3.For循环语法4.break语句和continue语句一、实验目的 掌握循环结构的语法 二、实验原理 Python中的循环语句有 for 和 while。 Python…

AcWing蓝桥杯AB组辅导课07、贪心

文章目录前言一、贪心模板题例题1&#xff1a;AcWing 104. 货仓选址&#xff08;贪心&#xff0c;简单&#xff0c;算法竞赛进阶指南&#xff09;分析题解&#xff1a;贪心思路例题例题1&#xff1a;AcWing 1055. 股票买卖 II&#xff08;贪心、状态机&#xff0c;简单&#xf…