不断优化的素数算法

news2024/11/20 16:31:39

前言:素数判断是算法中重要的一环,掌握优秀的素数判断方法是算法player的必修课。本文介绍的是由简到繁的素数算法,便于初学者从入门到精通。


素数(质数):只能被 1 和它本身整除的数称作素数,如:2、3、5、7、11等

目录

  • 一、🍓 暴力算法 🍓
  • 二、🍉 埃氏筛法 🍉
  • 三、🍈 欧拉筛法 🍈

一、🍓 暴力算法 🍓

  暴力算法是利用循环,看 2 − n 2 -n 2n 之间是否有能被 n n n 整除的数,若有,则 n n n 就是素数,否则就不是。
  Java代码如下: 时间复杂度为 O ( n ) O(n) O(n)

	/**
     * 不停地判断 2 ~ n-1 之间是否有数可以被 n 整除
     * @param n 输入的数字
     * @return 返回true为素数,false不为素数
     */
    public static boolean isPrime(int n){
        for (int i= 2; i < n; i ++){
            //只要有数能被 n 整除,就返回false
            if(n % i == 0)
                return false;
        }
        //没有数能被 n 整除就返回true
        return true;
    }

简单优化:
  其实循环的范围可以缩小到 n 的平方根,这是数学定理,就不过多赘述,这样时间复杂度就减小到了 O ( n ) O(\sqrt{n}) O(n ) ,那么代码就改变为如下:

	/**
     * 不停地判断 2 ~ 根号 n 之间是否有数可以被 n 整除
     * @param n 输入的数字
     * @return 返回true为素数,false不为素数
     */
    public static boolean isPrime(int n){
    	//Math.sqrt()的作用是求平方根
        for (int i= 2; i <= Math.sqrt(n); i ++){
            //只要有数能被 n 整除,就返回false
            if(n % i == 0)
                return false;
        }
        //没有数能被n整除就返回true
        return true;
    }

小结: 暴力算法只适合单个或少量的素数判断,若判断某个范围之间有哪些素数,时间复杂度可能会达到 O ( n n ) O(n \sqrt{n} ) O(nn ) ,如下代码就会达到。

	//求 n 以内的所有素数
	for (int i = 2; i <= n; i++){
        if(isPrime(i))
            System.out.println(i);
    }

另外还有简单优化的方法,如:跳过偶数(2除外)的判断等就不再讲述

二、🍉 埃氏筛法 🍉

  埃氏筛法是求 n 以内所有素数的方法,把不大于根号 n 的所有素数的倍数剔除,剩下的就是素数。方法及例子如下:

求 20 以内的所有素数:

  1. 列出 2 以后的所有序列:
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
  2. 标记数列的第一个数为素数:
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
  3. 划掉被标记的数的所有倍数:
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
    2 3 5 7 9 11 13 15 17 19
  4. 标记剩下数列中当前标记的下一个数为素数,再次重复执行以上操作,最后标记的数应为 n \sqrt{n} n (向下取整):
    2 3 5 7 9 11 13 15 17 19   标记 3
    2 3 5 7 9 11 13 15 17 19   删除 3 的倍数
    2 3 5 7 11 13 17 19      得到新的数列
    2 3 5 7 11 13 17 19      下一个数 5 大于了 n \sqrt{n} n ,就不用再标记了,当前数列已是结果
  5. 最后剩下的数都是 n 以内的素数:
    2 3 5 7 11 13 17 19

说明:在暴力算法那里我们提到,判断素数只需要判断到是否能被 n \sqrt{n} n 整除即可,所以在埃氏算法中,我们只需要标记到 n \sqrt{n} n ,就能把所有非素数剔除

图解:我们可以借助一个 int 型数组,当前单元的值为 1 表示当前下标的数是素数,否则不是素数,我们是借助 0 来完成上面第三步的划掉倍数的操作。上面的例子最终得到的数组如下:
在这里插入图片描述

该图就表示:2 3 5 7 11 13 17 19 都是素数,其他都不是。虽然 0 和 1 下标上的元素也是 1,但我们可以控制下标从 2 开始,毕竟 2 是第一个素数

Java代码实现:

	/**
     * 埃氏筛法
     * @param n 要求的是 n 以内的素数
     * @return 返回 int 型数组,0 表示当前下标数字不是素数,1 则就是素数
     */
    public static int[] sieve(int n){
        int[] arr = new int[n+1];// +1是为了让下标范围是 0 ~ n
        //让数组元素都为 1,即初始都为素数
        for (int i = 0; i <= n; i++)
            arr[i] = 1;
        //从 2 开始,若为素数就标记,并标记它的倍数不是素数
        for (int i = 2;i <= Math.sqrt(n); i++){//只需要标记到根号 n 即可
            if (arr[i] == 1){//素数才标记,因为当前位置可能是被标记的 0
                //j += i 控制 i 的倍数都被标记 0
                for (int j = 2 * i;j <= n;j += i){
                    arr[j] = 0;
                }
            }
        }
        return arr;
    }

小结:埃氏筛法的时间复杂度为 O ( n ∗ l o g n ) O(n*log n) O(nlogn),相对于暴力算法有了优化,适用于 10 6 以内范围的数据处理。常见的应用有:n 以内范围的素数求总和区间内的素数

三、🍈 欧拉筛法 🍈

  欧拉筛法是埃氏筛法的升级版。在埃氏筛法中,很多数都会被标记多次不是素数,例如 10 会在标记素数 2 、5 的时候都被标记不是素数,欧拉筛法则让每个数只被标记一次不是素数。使自身的时间复杂度达到线性的 O ( n ) O(n) O(n)。算法及例子如下,具体概念及证明就不在本文阐述,有兴趣的小伙伴可以去搜一下:

求 20 以内的所有素数:红色表示素数蓝色表示不是素数

  1. 列出 2 以后的所有序列,初始都标记为素数:
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
  2. 依次判断数列的每一个数,若为素数,则将其存放在一个数组中:
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
  3. 依次标记当前数的所有已找到素数倍的数不是素数:(当前所有已找到素数只有 2,当前数为 2,那么标记 2 * 2 = 4 不是素数)
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
  4. 标记一次,就判断当前数是否是当前素数的倍数,是则停止标记非素数,进入到下一个数的判断(这是使每个数只被标记一次的关键)
  5. 重复以上操作,直到判断到最后一个数(20):
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  当前数为 3,是素数,先加入素数数组,当前素数有 2,3,则标记 6 和 9 不是素数
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  当前数为 4,不是素数,不加入数组,当前素数有 2,3,则标记 8 不是素数,因为4是2的倍数,就结束标记(否则会重复标记12),对应第4步,进入下一个数
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  当前数为 5,是素数,先加入数组,当前素数有 2,3,5,则标记 10 15 不是素数(25大于20,不用标记)
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  当前数为 6,不是素数,不加入数组,当前素数有 2,3,5,则标记 12 不是素数,因为 12 是 2 的倍数,就结束标记(可以看到前面阻止了对 12 的标记,避免和这里发生重复),进入下一个数
    后续操作与同理,最后会发现每个数只被标记了一次。最终的数如下:
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

说明:通过以上步骤可以得出,需要两个数组,一个存已找到的素数,一个标记各个数是否为素数的数组

Java代码实现:

	/**
     * 欧拉筛法求 n 以内的所有素数
     * @param isPrime 标记是否为素数的数组,1表示是素数,0则不是
     * @param prime 存放以求得的素数的数组
     * @param n 就是 n 本身
     * @return 返回一共有几个素数
     */
    public static int euler(int[] isPrime,int[] prime,int n){
        int cnt = 0;    //记录素数个数
        //让所有数组元素都为 1,即初始都为素数
        for (int i = 0;i <= n;i++)
            isPrime[i] = 1;
        //从 2 开始,若为素数就标记,并标记当前数的所有已找到素数倍的数不是素数
        for (int i = 2; i <= n; i++){
            //当前数为素数,则加入素数数组,并使 cnt计数加一
            if (isPrime[i] == 1) prime[++cnt] = i;
            //标记当前数的所有已找到素数倍的数不是素数
            for (int j = 1;j <= cnt && i * prime[j] <= n; j++){

                int k = i * prime[j];
                isPrime[k] = 0;
                //判断当前数是否是当前素数的倍数,是则停止标记非素数,进入到下一个数的判断
                //这里是使复杂度降低在线性的关键
                if (i % prime[j] == 0) break;
            }
        }
        return cnt;
    }

小结:欧拉筛将复杂度降低到线性,较于其他两种算法有了非常大的提升,但是远不及埃氏筛法容易理解,需要细细品味。但是算法嘛,很多时候都是先背板子,再刷题理解去掌握的,多用就好了。

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

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

相关文章

【Linux学习】05-2Linux上部署项目

Linux&#xff08;B站黑马&#xff09;学习笔记 01Linux初识与安装 02Linux基础命令 03Linux用户和权限 04Linux实用操作 05-1Linux上安装部署各类软件 05-2Linux上部署项目 文章目录 Linux&#xff08;B站黑马&#xff09;学习笔记前言05-2Linux上部署项目部署Springboot项目…

【AI视野·今日Sound 声学论文速览 第二十期】Fri, 6 Oct 2023

AI视野今日CS.Sound 声学论文速览 Fri, 6 Oct 2023 Totally 6 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Sound Papers Deep Generative Models of Music Expectation Authors Ninon Liz Masclef, T. Anderson Keller对音乐的情感反应的一个重要理论围绕着惊喜…

时间序列常用数据处理

1.组合技巧Compose 1.2 实例应用及其解释 # 用于组合多个数据处理方法 class Compose(object):def __init__(self, transforms):self.transforms transformsdef __call__(self, seq):for t in self.transforms:seq t(seq)return seq 这段Python代码定义了一个名为Compose的…

星火大模型AI接口Spring中项目中使用【星火、AIGC】

星火大模型AI接口使用 讯飞的星火大模型是有免费版本赠送200万个Token&#xff0c;就个人学习来说完全够用了。 免费申请过后&#xff0c;到控制台&#xff0c;两个红色方框是最要紧的。 星火认知大模型Web文档 | 讯飞开放平台文档中心 (xfyun.cn)这是官方文档对于接口的详细使…

【AI视野·今日NLP 自然语言处理论文速览 四十九期】Fri, 6 Oct 2023

AI视野今日CS.NLP 自然语言处理论文速览 Fri, 6 Oct 2023 Totally 44 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers MathCoder: Seamless Code Integration in LLMs for Enhanced Mathematical Reasoning Authors Ke Wang, Houxi…

如何一键转发朋友圈,快速跟圈?

你是否曾为在微信上快速转发别人的朋友圈而烦恼&#xff1f;每天都要花费大量时间下载商品图片和复制粘贴商家的文案&#xff0c;让人疲惫不堪。我觉得这样太繁琐太麻烦了&#xff0c;每天都会上新货&#xff0c;上传朋友圈都要花将近一个小时的时间&#xff0c;花了大量时间在…

【C++设计模式之状态模式:行为型】分析及示例

简介 状态模式&#xff08;State Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象在内部状态改变时改变其行为&#xff0c;看起来就像是改变了其类。状态模式将对象的状态封装成不同的类&#xff0c;并使得对象在不同状态下有不同的行为。 描述 状态模式通过…

C++ 类和对象篇(五) 析构函数

目录 一、概念 1. 析构函数是什么&#xff1f; 2. 为什么要有析构函数&#xff1f; 3. 怎么用析构函数&#xff1f; 3.1 创建析构函数 3.2 调用析构函数 二、特性 三、由编译器生成的默认析构函数 四、对象的析构顺序 1. 局部对象 2. new出来的堆对象 3. 全局对象 一、概念 1…

Linux指令示范(1)

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇

小程序支付升级:实现微信支付V3接口接入

文章目录 用户付款流程业务流程讲解接入前准备快速接入1、引入开发库2、配置参数3、初始化商户配置4、微信支付对接5、支付回调-支付通知API 相较于 v2 版本&#xff0c;v3 版本的接口文档在阅读上可能显得相对凌乱。它的组织结构可能不太清晰&#xff0c;难以快速理解整个流程…

【Go语言实战】(25) 分布式算法 MapReduce

MapReduce 写在前面 身为大数据专业的学生&#xff0c;其实大学我也多多少少接触过mapreduce&#xff0c;但是当时觉得这玩意太老了&#xff0c;觉得这和php一样会被时代淘汰。只能说当时确实太年轻了&#xff0c;没有好好珍惜那时候的学习资源… 现在回过头来看mapreduce&a…

聊聊分布式架构——RPC通信原理

目录 RPC通信的基本原理 RPC结构 手撸简陋版RPC 知识点梳理 1.Socket套接字通信机制 2.通信过程的序列化与反序列化 3.动态代理 4.反射 思维流程梳理 码起来 服务端时序图 服务端—Api与Provider模块 客户端时序图 RPC通信的基本原理 RPC&#xff08;Remote Proc…

【算法练习Day13】二叉树的层序遍历翻转二叉树对称二叉树

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 二叉树的层序遍历翻转二叉树…

安装Ubuntu提示:系统找不到指定的文件。

今天我删除Ubuntu后重新下载&#xff0c;发现报错&#xff0c;错误信息如下&#xff1a; 这是因为系统没有卸载干净而导致的。 解决办法&#xff1a; 第一步&#xff1a; ##查询当前已安装的系统 wsl.exe --list --all 执行结果&#xff1a; 第二步&#xff1a; ##注销当前…

【GSEP202303 C++】1级 长方形面积

[GSEP202303 一级] 长方形面积 题目描述 小明刚刚学习了如何计算长方形面积。他发现&#xff0c;如果一个长方形的长和宽都是整数&#xff0c;它的面积一定也是整数。现在&#xff0c;小明想知道如果给定长方形的面积&#xff0c;有多少种可能的长方形&#xff0c;满足长和宽…

BF算法详解(JAVA语言实现)

目录 BF算法的介绍 图解 JAVA语言实现 BF算法的时间复杂度 BF算法的介绍 BF算法&#xff0c;即暴力(Brute Force)算法&#xff0c;是普通的模式匹配算法&#xff0c;BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配&#xff0c;若相等&#xff0c;则继…

C++设计模式-桥接(Bridge)

目录 C设计模式-桥接&#xff08;Bridge&#xff09; 一、意图 二、适用性 三、结构 四、参与者 五、代码 C设计模式-桥接&#xff08;Bridge&#xff09; 一、意图 将抽象部分与它的实现部分分离&#xff0c;使它们都可以独立地变化。 二、适用性 你不希望在抽象和它…

[笔记] Microsoft Windows网络编程《三》网际协议

文章目录 前言3.1 IPv43.1.1 寻址3.1.1.1 单播3.1.1.2 多播(组播)3.1.1.3 广播 3.1.2 IPv4 管理协议&#xff08;ARP&#xff0c;ICMP&#xff0c;IGMP&#xff09;ARPICMPIGMP 3.1.3 Winsock 中的IPv4 寻址 3.2 IPv63.2.1 寻址3.2.1.1 单播链接——本地地址站点——本地地址&a…

ipa文件怎么把应用上架到苹果ios系统下载的App Store商城

注册为苹果开发者&#xff1a;首先&#xff0c;您需要注册为苹果开发者。前往苹果开发者网站&#xff08;https://developer.apple.com/&#xff09;&#xff0c;点击"Enroll"按钮&#xff0c;并按照相关步骤注册和付费&#xff08;开发者账号需要年度费用&#xff0…

【Java 进阶篇】使用 JDBCTemplate 执行 DQL 语句详解

在前面的文章中&#xff0c;我们已经学习了如何使用 Spring 的 JDBCTemplate 执行 DML&#xff08;Data Manipulation Language&#xff09;操作&#xff0c;包括插入、更新和删除操作。现在&#xff0c;让我们来深入了解如何使用 JDBCTemplate 执行 DQL&#xff08;Data Query…