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

目录

方法一:暴力法

复杂度

方法二:跨度为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.mfbz.cn/a/578961.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Nacos 集群 On K8s 实践服务注册发现、服务动态配置

一、K8s 部署 Nacos 集群 安装规划 组件replicas类型mysql1StatefulSetnacos3StatefulSet 使用 k8s 版本为&#xff1a;v1.18.0 。 本次使用 OpenEBS 来作为存储引擎&#xff0c;OpenEBS 是一个开源的、可扩展的存储平台&#xff0c;它提供了一种简单的方式来创建和管理持久…

JavaEE——Spring Boot入门

目录 &#x1f4da; JavaEE——Spring Boot入门 &#x1f527; 1. 新建Spring Boot项目 &#x1f6e0; 2. 添加pom依赖 &#x1f4dd; 3. 添加application.yml文件 &#x1f4c2; 4. 创建Dao层 &#x1f527; 5. 创建Service层 &#x1f5a5;️ 6. 创建Controller层及HT…

easyExcel快速入门

目录 &#x1f9c2;1.简单介绍 &#x1f32d;2.快速入门 &#x1f953;1.导入依赖 &#x1f37f;2.导出到excel &#x1f38f;3.读入数据 &#x1f389;4.下载 1.简单介绍 传统操作Excel大多都是利用Apach POl进行操作的,但是POI框架并不完善,使用过程非常繁琐且有较多…

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.…

多端文件互传软件-LocalSend

一、前言 日常学习或者是工作需求&#xff0c;需要手机和电脑互传文件。用到频率低的话&#xff0c;使用即时通讯软件也就够了。 像我日常使用的多端互传文件软件是LocalSend。 二、 LocalSend LocalSend是一款基于局域网的文件传输工具。 LocalSend是一种用于在本地网络中…

super与this

目录 原型链与继承继承中的原型链 classsuper与this 我们可能会对一个问题感到好奇&#xff1a;为什么在派生类中&#xff0c;我们需要在调用this之前调用super。我们通常将其视为一种规范&#xff0c;却很少深入探究这个规范的真正意义。许多人认为super不过是ES6之前继承方式…

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…

每日算法之两两交换链表中的节点

题目描述 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4] 输出&…

sheng的学习笔记-AI-支持向量机(SVM)

目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 目录 什么是向量机 SVM算法原理 SVM基本模型 SVM对偶问题 什么是对偶问题&#xff1a; 为什么使用对偶问题 拉格朗日定理 拉格朗日乘子法 对偶问题算法 非线性SVM算法原理 核函数 常用核函数 软间隔与正则化 软…

RabbitMQ-死信队列

面试题&#xff1a;你们是如何保证消息不丢失的&#xff1f; 1、什么是死信 在 RabbitMQ 中充当主角的就是消息&#xff0c;在不同场景下&#xff0c;消息会有不同地表现。 死信就是消息在特定场景下的一种表现形式&#xff0c;这些场景包括&#xff1a; 1. 消息被拒绝访问&…

教你一分钟快速部署 Llama3 中文大模型

之前百度创始人李彦宏先生曾经说过“开源大模型会越来越落后&#xff0c;闭源模型会持续领先”&#xff0c;但国货表示真的不服&#xff0c;紧接着被扎克伯格同学就给了当头一棒&#xff0c;向他展示了什么叫做顶级开源大模型。那变听我娓娓道来。 美国当地时间4月18日&#x…

使用NGINX做局域网内 浏览器直接访问链接 拓展外网链接访问本地

达成目的功能&#xff1a; 在本地服务的一个文件路径下&#xff0c;局域网内用ip和路径名访问到对应的地址&#xff1b;如 10.5.9.0/v1 即可访问到 某个固定本地地址目录 V1下&#xff0c;名为index.html的文件。前言 NGINX 是一个非常流行的开源 Web 服务器和反向代理服务器…

5分钟梳理银行测试,文末附带实战项目,0经验入行so easy

很多银行招聘都要求有相关从业经验&#xff0c;这对于想跨入这个岗位的0经验从业同学可真犯了难 “你都不让我上岗&#xff0c;我哪来的工作经验呢&#xff1f;” 为了解决这个问题&#xff0c;小编整理了本篇文章&#xff0c;从3个方面介绍银行项目是如何进行测试的 银行的…

思维+线性dp,CF573 B. Bear and Blocks

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 573B - Codeforces 二、解题报告 1、思路分析 本题给的图还是很直…

制糖工业智能工厂数字孪生可视化平台,推进制糖产业数字化转型

制糖工业智能工厂数字孪生可视化平台&#xff0c;推进制糖产业数字化转型。随着信息技术的快速发展&#xff0c;数字化转型已成为各行各业的重要趋势。在糖果加工制造领域&#xff0c;智能工厂数字孪生可视化平台的出现&#xff0c;为行业数字化转型注入了新的活力。 糖果加工制…

应用于智能装备制造,钡铼IOy系列模块展现其强大的灵活性和实用性

随着科技的飞速发展&#xff0c;智能制造已经成为工业4.0时代的核心驱动力。在此背景下&#xff0c;钡铼技术推出的IOy系列模块以其独特的设计、卓越的性能以及无可比拟的灵活性与实用性&#xff0c;在智能装备制造领域展现出了强大的技术优势和应用价值。 首先&#xff0c;钡…

Redis面试题二(数据存储)

目录 1.redis 的数据过期策略 1. 惰性删除&#xff08;Lazy Expiration&#xff09; 2. 定期删除&#xff08;Periodic Expiration&#xff09; 3. 定时删除&#xff08;Timing-Based Expiration&#xff09; 实际应用中的组合策略 2.redis 有哪些内存淘汰机制 volatile&…

Maven解决找不到依赖项

报错如图 方案一&#xff1a;Maven的Setting文件中添加albaba的镜像文件 1.下载maven &#xff1a;Maven – Download Apache Maven 2. 配置镜像 更改成这个&#xff1a; <mirror> <id>alimaven</id> <name>aliyun maven</name> <url&g…

37.WEB渗透测试-信息收集-企业信息收集(4)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;36.WEB渗透测试-信息收集-企业信息收集&#xff08;3&#xff09;-CSDN博客 关于主域名收…

c#学习入门2

十、运算符 1&#xff09;算术运算符是用于数值类型变量计算的运算符&#xff0c;它返回的结果是数值 1.赋值符号 2.算数运算符 加 减- 乘* 除/ 取余% 3.算数运算符的优先级 4.算术运算符的复合运算 5.算术运算符的自增减 2&#xff09;字符串拼接 1.字符串拼接方式1 注意&…
最新文章