判断n以内的素数个数的五种方法+时间对比

news2024/11/20 6:26:36

目录

方法一:暴力法

复杂度

方法二:跨度为6的倍数的优化

复杂度

方法三:埃氏筛法

复杂度

方法四:埃氏筛法的改良

复杂度

方法五:线性筛

复杂度

性能对比测试

练习


方法一:暴力法

就是写一个函数isprime(int x),依次判断(2,根号x)之内的数能否被x整除。然后依次调用isprime(2,n-1)。

class Solution_1 {//暴力法
public:
    bool isprime_(int x) {
        for (int i = 2; i * i <= x; i++)
            if (x % i == 0) return false;
        return true;
    }
    int countPrimes(int n) {
        int ans = 0;
        for (int i = 2; i < n; i++)
            if (isprime_(i)) ans++;
        return ans;
    }
};

复杂度

时间 O( n*\left ( n^{1/2} \right ) )

空间 O(1)

方法二:跨度为6的倍数的优化

除了2,3之外,其它所有的素数(假设为x)余n后,要么是5,要么是1。

为什么呢?我们假设一个数为x

x%6==0 --> x是0或6的倍数,全都不是素数

x%6==2--> x是2的倍数,其中只有2是素数

x%6==3 --> x是3的倍数,其中只有3是素数

x%6==4 --> x是2的倍数且x>=4,全都不是素数

x%6==1 或 ||x%6==5 --> x有可能是素数

所以我们在isprime(x)函数中,在判断完特例2,3之后,从5开始,每次+6,判断能否被x整除。

同时,调用isprime函数时,也可以跨六步长。

class Solution_2 {//跨度为6的倍数的优化
public:
    bool isprime(int x) {
        if(x<=3) return x>1;
        if (x % 2 == 0 || x % 3 == 0) return false;
        for (int i = 5; i * i <= x; i += 6)
            if (x % i == 0 || x % (i + 2) == 0)
                return false;
        return true;
    }
    int countPrimes(int n) {
        if (n <= 2) return 0;
        if (n == 3) return 1;
        if (n == 4) return 2;
        int ans = 2;
        for (int i = 5; i < n; i += 6) {
            if (isprime(i)) ans++;
            if (i + 2 < n && isprime(i + 2)) ans++;
        }
        return ans;
    }
};

复杂度

时间 O(\frac{1}{6}*n*\frac{1}{6}*n^{1/2})

空间 O(1)

方法三:埃氏筛法

核心思想就是:1、如果x是质数,那么x的倍数肯定不是质数。

                        2、如果x是合数,那么x一定是某个小于x的质数y的倍数。(这就当个结论吧)

class Solution_3 {//埃氏筛法
public:
    int countPrimes(int n) {
        vector<bool> isprime(n, true);
        int ans = 0;
        for (int i = 2; i < n; i++) {
            if (isprime[i] == true) {
                ans++;
                for (int j = 2*i; j < n; j += i)
                    isprime[j] = false;
            }
        }
        return ans;
    }
};

复杂度

时间:O(n*\log \log n

空间:O(n)

方法四:埃氏筛法的改良

观察上面的式子会发现,有些数会重复被标记。

比如说代码中的i==11时,我们从isprime[11]开始标记,isprime[11],isprime[22],……,isprime[121],……都被标记为false。

但是对于isprime[11*2],……,isprime[11*10]这9个位置来说,分别在i==2,3,4,5,6,7,8,9,10的时候就已经被标记了。有的位置甚至被重复标记不止一次,比如说isprime[11*6],在i==2,i==3的时候都被标记了一遍,现在i==11还要被标记一遍。

为了防止以上i*i之前被重复标记的情况,在判断一个数是素数后。我们从i*i开始标记,而不是从2*i开始标记。

注意:i*i可能会造成isprime的数组越界,所以要转换成long long。

class Solution_4 {//埃氏筛法的优化
public:
    int countPrimes(int n) {
        int ans = 0;
        vector<bool> isprime(n, true);
        for (int i = 2; i < n; i++) {
            if (isprime[i]) {
                ans++;
                long long square = (long long)i * i;
                if (square < n) {
                    for (long long j = square; j < n; j += i)
                        isprime[j] = false;
                }
            }
        }
        return ans;
    }
};

复杂度

时间:O(n*\log \log n

空间:O(n)

方法五:线性筛

尽管埃氏筛的改良减少了一些重复标记的情况,但是还会有重复标记的情况。

埃氏筛的改良只能减少i*i前面的数的重复标记的情况,却不能减少i*i后面的数的重复标记的情况。

比如说isprime[45]这个位置,即使用埃氏筛的改良,还是会在i==3和i==5的时候都被标记。

因此我们要想一种方法让每一个数都只被标记一次。

算法的原理可以看力扣上第204题的题解

class Solution_5 {//线性筛法
public:
    int countPrimes(int n) {
        vector<int> primes;
        vector<int> isPrime(n, 1);
        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]] = 0;
                if (i % primes[j] == 0) {
                    break;
                }
            }
        }
        return primes.size();
    }
};

复杂度

时间:O(n)

空间:O(n)

性能对比测试

#include <iostream>
#include<vector>
#include<chrono>
using namespace std;

class Solution_1 {//暴力法
public:
    bool isprime_(int x) {
        for (int i = 2; i * i <= x; i++)
            if (x % i == 0) return false;
        return true;
    }
    int countPrimes(int n) {
        int ans = 0;
        for (int i = 2; i < n; i++)
            if (isprime_(i)) ans++;
        return ans;
    }
};

class Solution_2 {//跨度为6的倍数的优化
public:
    bool isprime(int x) {
        if(x<=3) return x>1;
        if (x % 2 == 0 || x % 3 == 0) return false;
        for (int i = 5; i * i <= x; i += 6)
            if (x % i == 0 || x % (i + 2) == 0)
                return false;
        return true;
    }
    int countPrimes(int n) {
        if (n <= 2) return 0;
        if (n == 3) return 1;
        if (n == 4) return 2;
        int ans = 2;
        for (int i = 5; i < n; i += 6) {
            if (isprime(i)) ans++;
            if (i + 2 < n && isprime(i + 2)) ans++;
        }
        return ans;
    }
};

class Solution_3 {//埃氏筛法
public:
    int countPrimes(int n) {
        vector<bool> isprime(n, true);
        int ans = 0;
        for (int i = 2; i < n; i++) {
            if (isprime[i] == true) {
                ans++;
                for (int j = 2*i; j < n; j += i)
                    isprime[j] = false;
            }
        }
        return ans;
    }
};

class Solution_4 {//埃氏筛法的优化
public:
    int countPrimes(int n) {
        int ans = 0;
        vector<bool> isprime(n, true);
        for (int i = 2; i < n; i++) {
            if (isprime[i]) {
                ans++;
                long long square = (long long)i * i;
                if (square < n) {
                    for (long long j = square; j < n; j += i)
                        isprime[j] = false;
                }
            }
        }
        return ans;
    }
};

class Solution_5 {//线性筛法
public:
    int countPrimes(int n) {
        vector<int> primes;
        vector<int> isPrime(n, 1);
        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]] = 0;
                if (i % primes[j] == 0) {
                    break;
                }
            }
        }
        return primes.size();
    }
};
int main()
{
    int n=867896;
    cout<<"测试数据:n="<<n<<endl;

    cout << "暴力法\t";
    auto start1 = chrono::system_clock::now();
    Solution_1 s1;
    cout << s1.countPrimes(n) << endl;
    auto end1 = chrono::system_clock::now();
    auto duration1 = chrono::duration_cast<chrono::microseconds>(end1 - start1);
    cout<<"暴力法耗时:\t"<<duration1.count()<<"微秒"<<endl;

    cout << "跨度为6的倍数的优化\t";
    auto start2 = chrono::system_clock::now();
    Solution_2 s2;
    cout << s2.countPrimes(n) << endl;
    auto end2 = chrono::system_clock::now();
    auto duration2 = chrono::duration_cast<chrono::microseconds>(end2 - start2);
    cout<<"跨度为6的倍数的优化耗时:\t"<<duration2.count()<<"微秒"<<endl;
    
    cout << "埃氏筛法\t";
    auto start3 = chrono::system_clock::now();
    Solution_3 s3;
    cout << s3.countPrimes(n) << endl;
    auto end3 = chrono::system_clock::now();
    auto duration3 = chrono::duration_cast<chrono::microseconds>(end3 - start3);
    cout<<"埃氏筛法耗时:\t"<<duration3.count()<<"微秒"<<endl;
    
    
    cout << "埃氏筛法的优化\t";
    auto start4 = chrono::system_clock::now();
    Solution_4 s4;
    cout << s4.countPrimes(n) << endl;
    auto end4 = chrono::system_clock::now();
    auto duration4 = chrono::duration_cast<chrono::microseconds>(end4 - start4);
    cout<<"埃氏筛法的优化耗时:\t"<<duration4.count()<<"微秒"<<endl;
    
    cout << "线性筛法\t";
    auto start5 = chrono::system_clock::now();
    Solution_5 s5;
    cout << s5.countPrimes(n) << endl;
    auto end5 = chrono::system_clock::now();
    auto duration5 = chrono::duration_cast<chrono::microseconds>(end5 - start5);
    cout<<"线性筛法耗时:\t"<<duration5.count()<<"微秒"<<endl;
    
    
    
    return 0;

}

测试结果

测试数据:n=867896
暴力法  68937
暴力法耗时:    84030微秒
跨度为6的倍数的优化     68937
跨度为6的倍数的优化耗时:       29138微秒
埃氏筛法        68937
埃氏筛法耗时:  739268微秒
埃氏筛法的优化  68937
埃氏筛法的优化耗时:    595306微秒
线性筛法        68937
线性筛法耗时:  39351微秒

练习

https://leetcode.cn/problems/count-primes/

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

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

相关文章

STL--string详解

STL基本内容 string是什么 string实质上是一个对象 string可看作一个串&#xff0c;类似字符数组 可以扩容&#xff0c;可以增删查改 可用下表访问操作符[]引用&#xff0c;修改某值 构造函数 默认构造 拷贝构造&#xff1a;参数为(string 或 char*) 求string对象的长度不…

锂电池SOH预测 | 基于SVM的锂电池SOH预测(附matlab完整源码)

锂电池SOH预测 锂电池SOH预测完整代码锂电池SOH预测 锂电池的SOH(状态健康度)预测是一项重要的任务,它可以帮助确定电池的健康状况和剩余寿命,从而优化电池的使用和维护策略。 SOH预测可以通过多种方法实现,其中一些常用的方法包括: 容量衰减法:通过监测电池的容量衰减…

C语言学习/复习36

一、程序的环境与预处理 二、翻译环境与执行环境 三、运行环境 四、预编译(预处理)详解

【Hadoop】-Apache Hive使用语法与概念原理[15]

一、数据库操作 创建数据库 create database if not exists myhive; 使用数据库 use myhive; 查看数据库详细信息 desc database myhive; 数据库本质上就是在HDFS之上的文件夹。 默认数据库的存放路径是HDFS的&#xff1a;/user/hive/warehouse内 创建数据库并指定hdfs…

SVN--基本原理与使用(超详细)

目录 一、SVN概述二、SVN服务端软件安装三、SVN服务端配置四、SVN客户端软件安装与使用五、SVN三大指令六、SVN图标集与忽略功能6.1 图标集6.2 忽略功能 七、SVN版本回退八、SVN版本冲突九、SVN配置多仓库与权限控制9.1 配置多仓库9.2 权限控制 十、服务配置与管理十一、模拟真…

JAVASCRIPT+PHP+GB2312字库文件实现浏览器LED滚动效果

一、效果 二、源码 1、test_led.html <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>MATRIX LED</title> <script src"https://cdn.staticfile.net/jquery/1.10.2/jquery.min.js"></script…

渗透攻击思考题

目录 问题一&#xff1a; 存储过程&#xff1a; 密文存储&#xff1a; 问题二&#xff1a; 问题三&#xff1a; 问题四&#xff1a; LM Hash的加密: NTLM Hash 的加密&#xff1a; 问题一&#xff1a; windows登录的明文密码&#xff0c;存储过程是怎么样的&#xff0…

数据聚类:Mean-Shift和EM算法

目录 1. 高斯混合分布2. Mean-Shift算法3. EM算法4. 数据聚类5. 源码地址 1. 高斯混合分布 在高斯混合分布中&#xff0c;我们假设数据是由多个高斯分布组合而成的。每个高斯分布被称为一个“成分”&#xff08;component&#xff09;&#xff0c;这些成分通过加权和的方式来构…

mysql-connector-java和spring-boot-starter-jdbc和mybatis-spring-boot-start

mysql-connector-java和spring-boot-starter-jdbc和mybatis-spring-boot-start JDBC是什么意思&#xff1f; JDBC是使用java语言操作mysql数据库的规范&#xff0c;java语言必须按照这个规范写才可以操作mysql数据库。 mysql-connector-java 在最开始的时候 程序中是不允许…

深入解析AI绘画算法:从GANs到VAEs

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

蛋糕购物商城

蛋糕购物商城 运行前附加数据库.mdf&#xff08;或使用sql生成数据库&#xff09; 登陆账号&#xff1a;admin 密码&#xff1a;123456 修改专辑价格时去掉&#xffe5;以及上传专辑图片 c#_asp.net 蛋糕购物商城 网上商城 三层架构 在线购物网站&#xff0c;电子商务系统 …

Linux——终端

一、终端 1、终端是什么 终端最初是指终端设备&#xff08;Terminal&#xff09;&#xff0c;它是一种用户与计算机系统进行交互的硬件设备。在早期的计算机系统中&#xff0c;终端通常是一台带有键盘和显示器的电脑&#xff0c;用户通过它输入命令&#xff0c;计算机在执行命…

redisson分布式锁的单机版应用

package com.redis;/*** author linn* date 2024年04月23日 15:31*/ import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.context.annotation.Bean; import org.springframework.context.…

SpringBoot 3.2.5 引入Swagger(OpenApi)

SpringBoot 3.2.5 引入Swagger&#xff08;OpenApi&#xff09; pom文件配置文件启动类Controller 层ApiFox题外话 springdoc-openapi 和 swagger 都可以用&#xff0c;用其中一个就行&#xff0c;不用两个都引入。 这里简单记录以下springdoc-openapi。 springdoc-openapi(J…

【AI相关】模型相关技术名词

目录 过拟合和欠拟合 1.过拟合 2.欠拟合 特征清洗、数据变换、训练集、验证集和测试集 1.特征清洗 2.数据变换 3.训练集 4.验证集 5.测试集 跨时间测试和回溯测试 1.跨时间测试&#xff08;OOT 测试&#xff09; 2.回溯测试 联合建模与联邦学习 1.联合建模 2.联…

用友政务财务系统FileDownload接口存在任意文件读取漏洞

声明&#xff1a; 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 简介 用友政务财务系统是由用友软件开发的一款针对政府机…

OPPO手机支持深度测试+免深度测试解锁BL+ROOT权限机型整理-2024年3月更新

绿厂OPPO手机线上线下卖的都很不错&#xff0c;目前市场份额十分巨大&#xff0c;用户自然也非常多&#xff0c;而近期ROM乐园后台受到很多关于OPPO手机的私信&#xff0c;咨询哪些机型支持解锁BL&#xff0c;ROOT刷机&#xff0c;今天ROM乐园正式盘点当前市场上可以解BL刷root…

树莓派4-通过IIC实现图片循环播放

一、环境 1、树莓派4&#xff1b; 2、串口连接电脑&#xff1b; 3、树莓派由杜邦线连接0.96寸OLED1306协议 4、树莓派能够联网&#xff0c;便于安装环境。离线情况也可以安装&#xff0c;相对麻烦&#xff1b; 二、目标 1、树莓派可以开启IIC并识别已连接的IIC&#xff1b; …

机器人-轨迹规划

旋转矩阵 旋转矩阵--R--一个3*3的矩阵&#xff0c;其每列的值时B坐标系在A坐标系上的投影值。 代表B坐标系相对于A坐标系的姿态。 旋转矩阵的转置矩阵 其实A相对于B的旋转矩阵就相当于把B的列放到行上就行。 视频 &#xff08;将矩阵的行列互换得到的新矩阵称为转置矩阵。&…

4月26日 阶段性学习汇报

1.毕业设计与毕业论文 毕业设计已经弄完&#xff0c;加入了KNN算法&#xff0c;实现了基于四种常见病的判断&#xff0c;毕业论文写完&#xff0c;格式还需要调整&#xff0c;下周一发给指导老师初稿。目前在弄答辩ppt&#xff08;25%&#xff09;。25号26号两天都在参加校运会…