三十三、数学知识——质数(朴素筛法 + 埃氏筛法 + 线性筛法)

news2024/10/6 14:37:46

质数与质数筛法算法主要内容

  • 一、基本思路
    • 1、质数
        • 质数的判定——试除法(复杂度固定位 O(sqrt(n)) )
    • 2、分解质因数——试除法(最坏是O(sqrt(n)))
    • 3、朴素筛法——筛的是倍数
    • 4、埃氏筛法——朴素筛法优化
    • 5、线性筛法——n(遍历值)只会被最小质因子筛掉
  • 二、Java、C语言模板实现
  • 三、例题题解
        • 1. 质数判定
        • 2.分解质因数
        • 3. 筛法——筛出1~n个质数(埃氏筛法与线性筛法)

一、基本思路

1、质数

  • 定义:
    • 在大于 1 的整数中,如果只包含 1 和本身这两个约数,就被称为质数,或者叫素数,与之相反的是合数。

质数的判定——试除法(复杂度固定位 O(sqrt(n)) )

  • 概述:
    • 实际是一个找因数的过程,只不过通过条件判定之后,确定只有 1 与本身外,才可以确定为质数。
  • 原理:
    1. 定义一个符号:“||” 称为整除, a||b:即a为b的约数之一,b除以a可以得到一个整数。
    1. 例:若d||n,则(n||d)|| n,我们只需要取得比较小的那个约数就好了。
    1. 用if(n%i == 0)来判断是否是质数,若满足,则不是质数——>实际上也是在遍历因数。
    1. 原先根据定义,我们需要将约数遍历至d,但是根据上述理论,我们只需要遍历至d<=(n/d),即为 d.^2 <=n;
    1. for(int i = 2; i <= n/i; i++)是为了遍历寻找因数
    1. 注意:
      • 1)不用sqrt(n),是因为sqrt比较慢。
      • 2)不用d.^2 <= n,是怕 d.^2太大而溢出。

2、分解质因数——试除法(最坏是O(sqrt(n)))

  • 时间复杂度:
    • 最好情况是logn(2的k次方),最坏是sqrt(n)
  • 目的:
    • 找出所有的因数中的质数/找出约数集合中的所有质数
  • 原理:
    • 正整数唯一分解定理(质因数分解定理)

在这里插入图片描述

  • 算法步骤:
for(int i = 2; i <= n/i; i++){
        // 问题:我们不应该÷的是质因子嘛?为什么是遍历全部?
        // 我们从质因子2开始由小变大,由于n/i的存在,
        // 所有由质因子构成的非质因子,在已经被质因子除掉,无须再考虑,因此此处遍历始终是质因子。
        
        // 定理:质因数分解定理
        // 任何一个大于1的正整数都可以唯一的分解成质数的乘积。
            if(n % i == 0){
                int s = 0;
                while(n % i ==0){
                    n /= i;             // n多次÷i,则由质数构成的合数被干掉了
                    s++;                // 记录质因子的质数(次数)
                }
                System.out.println(i + " " + s);
            }
        }
        
        if(n > 1){      
            // 此处判断是因为上面遍历到最后的n/i的时候,可能没有除尽,还剩下最后一个质数,需要判断输出
            System.out.println(n + " " + 1);
        }
  • 由 性质:n中最多只包含一个大于sqrt(n)的质因子,则优化为 for(int i = 2; i <= n/i; i++),只不过需要最后加一个:最后一步的 n/=i 之后的n值,输出最后一个质因子(if(n > 1) cout)

3、朴素筛法——筛的是倍数

  • 目的:
    • 求出 1~n中质数的个数
  • 基本思路:O(nlogn)
    • 从2开始进行遍历,遍历至n,将每一个遍历数的倍数删掉,剩下的即为质数。

在这里插入图片描述

4、埃氏筛法——朴素筛法优化

  • 优化:
    • 只筛掉其中质数的倍数即可,即 2~p-1之中的质数
    • 具体操作就是在质数判断中加入倍数筛除

5、线性筛法——n(遍历值)只会被最小质因子筛掉

  • 原理:
    • 使用最小质因子进行筛除
for(int i = 2; i <= n; i++){
   if(!st[i]){
       primes[count++] = i;            // 记录遍历过程中的质数
   }

   for(int j = 0; j <= n/i; j++){      // 筛掉其中质数构成的合数

      st[primes[j] * i] = true;       // 使用质因数prime来进行筛选
      // 当下面条件满足的时候,pjU一定是最小的质因子
      // 并且 pj 一定是 pj * i 的最小质因子
      if(i % primes[j] == 0){         
         // 当此处满足的时候,也就意味prime是i最小的质因数
         // 1、prime是从小到大存储的,因此一定是最小的
         // 2、当满足条件的时候,直接i的继续筛的过程结束,找到第一个质因数,则跳过,始终满足最小
            break;
       }
    }
}

  • 注意:
    1. i % prime[j] == 0 ,prime[j] 一定是 i 的最小质因子,pj一定是 pj * i 的最小质因子。
    1. i % prime[j] == 0,pj 一定小于所有质因子(break的存在,找到则跳出),pj也一定是 pj*i的最小质因子。
    1. 对于一个合数x,假设pj是x的最小质因子,当i美剧导 x/pj的时候,已经被筛掉
    1. 在10的7次方范围内,线性筛法比埃氏筛法快一倍。
    1. **综合1、2可知,在内层for循环里面,无论何时,prime[j]都是prime[j]*i的最小质因子,因此st[prime[j]*i] = true.

二、Java、C语言模板实现

// java 模板
// 1、质数判定
    static boolean isPrime(int n){

        if(n < 2){                      // 按照定义质数是大于 1 的整数
            return false;
        }

        for(int i = 2; i <= n/i; i ++){     
        // 此处之所以是 i <= n/i ,是因为我们只需要遍历最小的部分约数即可,其他可以由最小的一部分约数构成
            if(n % i == 0){         // 存在除了 1 和本身之外的约数
                return false;
            }
        }

        return true;
    }


// 2、朴素筛法/埃氏筛法

    static int N = 1000010;
    static boolean[] st = new boolean[N];
    static int primeNum;

    static void getPrimeNum(int n){

        for(int i = 2; i <= n; i++){                // 从2开始一直遍历到n-1的倍数
            if(st[i] == false){                     // 未被删掉,说明是质数
                primeNum++;
                for(int j = i + i; j <=n; j += i){  
                // 我们只需遍历质数的倍数就可以了,因为其他数都可以分解成质数
                    st[j] = true;
                }
            }
        }

        System.out.println(primeNum);

    }

// 3、线性筛法
    static void getPrime(int n){

        for(int i = 2; i <= n; i++){
            if(!st[i]){
                primes[count++] = i;            // 记录遍历过程中的质数
            }

            for(int j = 0; j <= n/i; j++){      // 筛掉其中质数构成的合数

                st[primes[j] * i] = true;       // 使用质因数prime来进行筛选
                // 当下面条件满足的时候,pjU一定是最小的质因子
                // 并且 pj 一定是 pj * i 的最小质因子
                if(i % primes[j] == 0){         
                // 当此处满足的时候,也就意味prime是i最小的质因数
                // 1、prime是从小到大存储的,因此一定是最小的
                // 2、当满足条件的时候,直接i的继续筛的过程结束,找到第一个质因数,则跳过,始终满足最小
                    break;
                }
            }
        }

        System.out.println(count);
    }



// C++ 模板,由yxc实现
// 1、试除法判定质数 —— 模板题 AcWing 866. 试除法判定质数
bool is_prime(int x)
{
    if (x < 2) return false;
    for (int i = 2; i <= x / i; i ++ )
        if (x % i == 0)
            return false;
    return true;
}


// 2、试除法分解质因数 —— 模板题 AcWing 867. 分解质因数
void divide(int x)
{
    for (int i = 2; i <= x / i; i ++ )
        if (x % i == 0)
        {
            int s = 0;
            while (x % i == 0) x /= i, s ++ ;
            cout << i << ' ' << s << endl;
        }
    if (x > 1) cout << x << ' ' << 1 << endl;
    cout << endl;
}


// 3、朴素筛法求素数 —— 模板题 AcWing 868. 筛质数
int primes[N], cnt;     // primes[]存储所有素数
bool st[N];         // st[x]存储x是否被筛掉

void get_primes(int n)
{
    for (int i = 2; i <= n; i ++ )
    {
        if (st[i]) continue;
        primes[cnt ++ ] = i;
        for (int j = i + i; j <= n; j += i)
            st[j] = true;
    }
}

// 4、线性筛法求素数 —— 模板题 AcWing 868. 筛质数
int primes[N], cnt;     // primes[]存储所有素数
bool st[N];         // st[x]存储x是否被筛掉

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;
        }
    }
}

三、例题题解

1. 质数判定

在这里插入图片描述

// java题解实现
import java.util.*;
import java.io.*;

public class Main{

    static boolean isPrime(int n){

        if(n < 2){                      // 按照定义质数是大于 1 的整数
            return false;
        }

        for(int i = 2; i <= n/i; i ++){     
        // 此处之所以是 i <= n/i ,是因为我们只需要遍历最小的部分约数即可,其他可以由最小的一部分约数构成
            if(n % i == 0){         // 存在除了 1 和本身之外的约数
                return false;
            }
        }

        return true;
    }


    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String[] str1 = reader.readLine().split(" ");

        int n = Integer.parseInt(str1[0]);

        for(int i = 0; i < n; i++){
            String str2 = reader.readLine();
            int ai = Integer.parseInt(str2);
            if(isPrime(ai)){
                System.out.println("Yes");
            }else{
                System.out.println("No");
            }
        }

    }

}

2.分解质因数

在这里插入图片描述

import java.util.*;
import java.io.*;


public class Main{
    
    static void getPrime(int n){
        
        for(int i = 2; i <= n/i; i++){
        // 问题:我们不应该÷的是质因子嘛?为什么是遍历全部?
        // 我们从质因子2开始由小变大,由于n/i的存在,
        // 所有由质因子构成的非质因子,在已经被质因子除掉,无须再考虑,因此此处遍历始终是质因子。
        
        // 定理:质因数分解定理
        // 任何一个大于1的正整数都可以唯一的分解成质数的乘积。
            if(n % i == 0){
                int s = 0;
                while(n % i ==0){
                    n /= i;             // n多次÷i,则由质数构成的合数被干掉了
                    s++;                // 记录质因子的质数(次数)
                }
                System.out.println(i + " " + s);
            }
        }
        
        if(n > 1){      
            // 此处判断是因为上面遍历到最后的n/i的时候,可能没有除尽,还剩下最后一个质数,需要判断输出
            System.out.println(n + " " + 1);
        }
        System.out.println();
    }
    
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String str1 = reader.readLine();
        
        int n = Integer.parseInt(str1);
        
        for(int i = 0; i < n; i++){
            String str2 = reader.readLine();
            int ai = Integer.parseInt(str2);
            getPrime(ai);
        }
    }
}

3. 筛法——筛出1~n个质数(埃氏筛法与线性筛法)

在这里插入图片描述

// 埃氏筛法
import java.util.*;

// 原理:假定有一个数p未被2~(p-1)中的数标记过,那么说明,不存在2~(p-1)中的任何一个数的倍数是p,
// 也就是说p不是2~(p-1)中的任何数的倍数,也就是说2~(p-1)中不存在p的约数,因此,根据质数的定义可知:
// p是质数.
public class Main{
    static int N = 1000010;
    static boolean[] st = new boolean[N];
    static int primeNum;

    static void getPrimeNum(int n){

        for(int i = 2; i <= n; i++){                // 从2开始一直遍历到n-1的倍数
            if(st[i] == false){                     // 未被删掉,说明是质数
                primeNum++;
                for(int j = i + i; j <=n; j += i){  
                // 我们只需遍历质数的倍数就可以了,因为其他数都可以分解成质数
                    st[j] = true;
                }
            }
        }

        System.out.println(primeNum);

    }
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        getPrimeNum(n);

    }
}


// 线性筛法
import java.util.*;

public class Main{
    static int N = 10000010;
    static int[] primes = new int[N];
    static boolean[] st = new boolean[N];
    static int count = 0;

    static void getPrime(int n){

        for(int i = 2; i <= n; i++){
            if(!st[i]){
                primes[count++] = i;            // 记录遍历过程中的质数
            }

            for(int j = 0; j <= n/i; j++){      // 筛掉其中质数构成的合数

                st[primes[j] * i] = true;       // 使用质因数prime来进行筛选
                // 当下面条件满足的时候,pjU一定是最小的质因子
                // 并且 pj 一定是 pj * i 的最小质因子
                if(i % primes[j] == 0){         
                // 当此处满足的时候,也就意味prime是i最小的质因数
                // 1、prime是从小到大存储的,因此一定是最小的
                // 2、当满足条件的时候,直接i的继续筛的过程结束,找到第一个质因数,则跳过,始终满足最小
                    break;
                }
            }
        }

        System.out.println(count);
    }


    public static void main(String[] args){
        Scanner in = new Scanner(System.in);

        int n = in.nextInt();

        getPrime(n);
    }
}

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

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

相关文章

刷题---C语言

目录 前言&#xff1a; 一.刷题&#xff08;1&#xff09; 1.1打印X图案 1.2打印带空格直角三角形图案 1.3小乐乐改数字 1.4牛牛的线段 2.刷题&#xff08;2&#xff09; 2.1判断奇偶性 2.2及格分数 2.3kiki算术 2.4&#xff08;ab-c&#xff09;*d 2.5KiKi算期末成…

亿级大表拆分过程记录

两年前接手公司的财务系统的开发和维护工作。在系统移交的初期&#xff0c;笔者和团队就发现&#xff0c;系统内有一张5000W的大表。 跟踪代码发现&#xff0c;该表是用于存储资金流水的表格&#xff0c;关联着众多功能点&#xff0c;同时也有众多的下游系统在使用这张表的数据…

Doris-----Aggregate 聚合模型及案例实现

Aggregate 模型 是相同key的数据进行自动聚合的表模型。表中的列按照是否设置了 AggregationType&#xff0c;分为 Key&#xff08;维度列&#xff09;和 Value&#xff08;指标列&#xff09;&#xff0c;没有设置 AggregationType 的称为 Key&#xff0c;设置了 Aggregation…

外包实在是太坑了,干了三年,感觉人都废了

先说一下自己的情况&#xff0c;专科生&#xff0c;19年通过校招进入杭州某个外包软件公司&#xff0c;干了接近3年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了3年的功…

【JMeter中的View Result Tree显示中文乱码】

JMeter中的View Result Tree显示中文乱码 检查JMeter的安装目录下的bin文件夹中的jmeter.properties配置文件 用记事本打开并搜索&#xff1a;sampleresult.default.encoding 找到该行 改成sampleresult.default.encodingutf-8 修改后重启JMeter ok, 解决乱码 附加 : 下载 J…

【Linux高级 I/O(7)】初识文件锁——fcntl()方法及其独占性、共享性实验(附全文代码)

fcntl()函数在前面系列内容中已经多次用到了&#xff0c;它是一个多功能文件描述符管理工具箱&#xff0c;通过配合不同的 cmd 操作命令来实现不同的功能。为了方便述说&#xff0c;这里再重申一次&#xff1a; #include <unistd.h> #include <fcntl.h>int fcntl(…

大模型对世界的改变,从一时一地,到无处不在、无时不有

作者 | 曾响铃 文 | 响铃说 大模型正在中国遍地开花&#xff0c;做过的没做过的都要过来参合一下。 汹涌浪潮中&#xff0c;不免有更多人开始关注那个最先发布的文心一言。 全球科技大厂中第一个发布GPT大模型产品的百度&#xff0c;在刚刚的中关村论坛上透露了一些文心一言…

nodejs连接mysql

npm i express #node后端框架npm i corsnpm i mysqlconst app require(express)(); const cors require(cors); const port 5000; const mysql require(mysql) //引入mysql 模块app.use(cors({}))const conn mysql.createConnection({user: root,password: qwertyuiop…

普通人想自学软件测试?我还是劝你算了吧。。。

本人7年测试经验&#xff0c;在学测试之前对电脑的认知也就只限于上个网&#xff0c;玩个办公软件。这里不能跑题&#xff0c;我为啥说&#xff1a;自学软件测试&#xff0c;一般人我还是劝你算了吧&#xff1f;因为我就是那个一般人&#xff01; 软件测试基础真的很简单&…

gtest单元测试

gtest单元测试 1. gtest是什么&#xff1f;简答&#xff1a;做测试用的2. gtest的优点3. 搭建测试框架4. gtest_范例演示 1. gtest是什么&#xff1f;简答&#xff1a;做测试用的 gtest是Google的一套用于编写C测试的框架&#xff0c;可以运行在很多平台上&#xff08;包括Lin…

【JavaSE】Java基础语法(十四):Static

文章目录 概述特点与应用注意事项为什么一个静态方法中只能访问用static修饰的成员? 概述 Java中的static是一个修饰符&#xff08;也可称关键字&#xff09;&#xff0c;可以用于修饰变量、方法和代码块。 特点与应用 static修饰的成员具有以下特点&#xff1a; 被类的所有对…

如何在Mac上抓取安卓设备的日志

要在 Mac 上抓取 Android 设备的日志&#xff0c;您可以使用 Android SDK 中的 adb 工具。以下是一个简单的步骤&#xff1a; 1.您需要在 Mac 上安装 Android SDK。您可以从 Android 开发者网站上下载最新版本的 Android SDK&#xff0c;并按照说明进行安装。 2.将您的 Andro…

重学 Symbol

重学 Symbol 之前在写基础类型的笔记时暂时性的先跳过了 symbol&#xff0c;现在也有了一些项目的使用经验后&#xff0c;觉得还是需要重新回滚并且学习一下&#xff0c;温故而知新。 首先依旧回顾一下 symbol 的特点&#xff1a; 是原始值 唯一 不可变 可以提供私有属性&…

javaWeb ssh沙发销售系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 java ssh沙发销售系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Mye…

【SUMO】SUMO运行自带的OSM入门教程

文章目录 一、运行CMD命令行二、进入OSM选择地图位置 首先给出官网教程&#xff1a; https://sumo.dlr.de/docs/Tutorials/OSMWebWizard.html 一、运行CMD命令行 代码&#xff1a; 先进入osmWebWizard.py文件地址 cd /d D:\SUMO\sumo-1.17.0\tools&#xff08;替换成自己的…

智慧PG(pgting),一款拖拽式智能页面搭建系统

目录 前言 一、介绍 二、设计理念 1&#xff0c;资源整合&#xff0c;开箱即用 2&#xff0c;降低系统颗粒度 3&#xff0c;组件共享 4&#xff0c;简化配置 三、系统功能 1&#xff0c;可视化大屏搭建&#xff1a; 四、技术架构 1&#xff0c;技术栈 2&#xff0c;整体架构 五…

LeetCode 1091. Shortest Path in Binary Matrix【BFS,A星】中等

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

C++ 代码整洁之道

NOTICE: 这篇文章的框架条目来自《C代码整洁之道&#xff1a;C17可持续软件开发模式实践》&#xff0c;作者: [德] 斯提芬罗特。书籍原名"Clean C: Sustainable Software Development Patterns and Best Practices with C 17"。 文章目录 编码基本原则保持简单和直接…

Unity | HDRP高清渲染管线学习笔记:示例场景解析

目录 一、HDRP入门 1.HDRP设置 1.1 HDRP配置文件中的全部设置项 1.1.1 Rendering下的Lit Shader Mode 1.1.2 Lighting 下的Volumetrics&#xff08;体积光&#xff09;和Screen Space Reflection&#xff08;屏幕空间反射&#xff09; 2.离线渲染VS实时渲染 3.Volume组件 …

文字gif闪图怎么做?高效的gif闪图制作方法

相信不少新媒体行业的小伙伴&#xff0c;一定都见过那种闪动文字效果的gif动图吧。效果非常的炫酷还很吸引人们的眼球&#xff0c;但是作为设计小白这种闪烁gif图要怎么制作呢&#xff1f;有没有那种小白也能轻松上手的工具呢&#xff1f; 一、什么样的工具能够在线生成gif动态…