素数之谜揭秘:一文详解试除法判断素数

这是我非常喜欢的一道编程题目。不要小看这道题,它看似简单,实则奥妙无穷。由于这是C语言的入门篇,只介绍最简单,也最容易想到的方法:试除法。但哪怕是试除法,也有不少变化。

要想了解试除法,首先要知道什么是素数。简单来说(可能不太严谨,不过我们只重视思路):素数就是质数,也就是只能被1和本身整除的数。

所以就诞生了判断素数最简单、最朴素的方法,假设要判断数i是不是素数,只需要拿2~(i-1)之间的数去试除i,如果其中有一个数被i整除,那么i就不是素数;反之,如果2~(i-1)之间的数都不能被i整除,就说明i是素数。

比如说,判断7是不是素数,发现2,3,4,5,6都不能被7整除,得出结论:7是素数。或者判断,15是不是素数,发现2不能被15整除,但试到3发现可以被15整除,说明15是合数,不是素数。

有了这些铺垫,就很容易做出下面这道编程题:

打印100到200之间的素数,并统计个数

完整代码如下:

#include <stdio.h>//打印100~200之间的素数,并统计个数
int main()
{int i = 0;int count = 0;//统计个数//产生100~200之间的整数for (i = 100; i <= 200; i++){int flag = 1;//假设i是素数//判断i是不是素数//拿2~i-1的数去试除i,如果整除就不是素数,如果都不整除就是素数//产生2~i-1的数int j = 0;for (j = 2; j < i; j++){if (i % j == 0){flag = 0;//当i不是素数时把j改为0break;}}if (1 == flag)//由于i没有被改成0,说明i是素数{count++;//输出素数printf("%d ", i);}}//输出个数printf("\ncount = %d\n", count);return 0;
}

这里先用for循环产生100~200之间的整数,然后产生2~(i-1)之间的数去试除i,如果整除就把flag置成0。当内层循环结束时,判断flag的值,如果是1,说明i没有被任何一个j整除,就说明i是素数;如果i是0,说明中间i被某一个j整除,从而说明i不是素数。

一定要用flag来判断是不是素数吗?其实不用。稍稍修改一下代码,就能把flag去掉

#include <stdio.h>//打印100~200之间的素数,并统计个数
int main()
{int i = 0;int count = 0;//统计个数//产生100~200之间的整数for (i = 100; i <= 200; i++){//判断i是不是素数//拿2~i-1的数去试除i,如果整除就不是素数,如果都不整除就是素数//产生2~i-1的数int j = 0;for (j = 2; j < i; j++){if (i % j == 0){break;}}if (i == j)//判断i是不是素数,如果2~(i-1)之间的数都不能被i整除,j则为i的值才跳出上面这个循环{count++;//输出素数printf("%d ", i);}}//输出个数printf("\ncount = %d\n", count);return 0;
}

但是去掉flag后代码变得不那么好理解,所以我还是倾向于有flag的版本,接下来我们对有flag的版本做一些优化,提高代码的效率。

首先,一个很容易想到的点是,所有的偶数都肯定不是素数,所以只需产生所有的奇数即可。

#include <stdio.h>int main()
{int i = 0;int count = 0;//统计个数//产生100~200之间的整数for (i = 101; i < 200; i += 2)//偶数肯定不是素数,所以只需产生100~200之间的奇数{int flag = 1;//假设i是素数//判断i是不是素数//拿2~i-1的数去试除i,如果整除就不是素数,如果都不整除就是素数//产生2~i-1的数int j = 0;for (j = 2; j < i; j++){if (i % j == 0){flag = 0;//当i不是素数时把j改为0break;}}if (1 == flag)//由于i没有被改成0,说明i是素数{count++;//输出素数printf("%d ", i);}}//输出个数printf("\ncount = %d\n", count);return 0;
}

接着就是重点了!首先存在下面这条定理(同样是不太严谨的表述,只是为了易于理解):如果一个数m能写成a*b的形式,那么a和b之中至少有一个数会小于或等于m的算术平方根!(定理1)

举个例子:100=4*25=10*10

当写成4*25时,4就比100的算术平方根(10)要小;当写成10*10时,两个因子都是10,都等于100的算术平方根(10)。

证明很简单,用反证法,假设两个因子都比m的算术平方根大,那么它们的乘积也会比m大,这与它们的乘积等于m矛盾!所以假设不成立,也就是说,至少有一个因子小于或等于m。

讲了一大堆,这根求解素数有什么关系呢?事实上,这个定理可以减少试除的次数!前面我们是用2~(i-1)的数去试除i,但其实只需要拿2~i的算术平方根的数去试除i就行了。

有朋友又纳闷了:为啥呢?很简单,如果2~i的算术平方根之间的数有一个能被i整除,很自然就说明了i是合数,不是素数,这个很好理解。

如果2~i的算术平方根之间的数都不能被i整除(命题1),那么i的算术平方根到(i-1)之间的数也不可能被i整除(命题2)。证明如下:仍然使用反证法,假设在满足命题1的前提下,命题2为假,也就是存在i的算术平方根到(i-1)之间的数能被i整除,也就是说,i能分解为两个整数的因子,那么根据定理1,就至少有一根因子介于2~i的算术平方根之间,也就是说,2~i的算术平方根之间的数一定至少存在一个数能被i整除,与命题1矛盾!所以假设不成立,也就是说,如果命题1成立,命题2一定成立!所以就不用把2~(i-1)之间的数全部试除一遍了,只需要拿2~i的算术平方根之间的数去试除就行了,大大节省了工作量。

比方说,本来要判断101是不是素数,需要拿2~100之间的数去试除,但是现在只需要拿2~101的算术平方根(10点几)之间的数去试除,也就是拿2到10之间的数去试除,效果可想而知。而且,每判断一个数就可以节省这么多计算量,整体效率就大大提升了。

优化后的代码如下:(有一个细节,上面这些只是理论上的论证,实际上由于我们已经从源头上去掉了所有的奇数,所以试除的数从3开始即可,无需从2开始)

#include <stdio.h>
#include <math.h>//打印100~200之间的素数,并统计个数
int main()
{int i = 0;int count = 0;//统计个数//产生100~200之间的整数for (i = 101; i < 200; i += 2)//偶数肯定不是素数,所以只需产生100~200之间的奇数{int flag = 1;//假设i是素数//判断i是不是素数//拿2~i-1的数去试除i,如果整除就不是素数,如果都不整除就是素数//但是事实上只需要拿2~i的算术平方根之间的数去试除i//产生2~i的算数平方根int j = 0;for (j = 3/*其实这里从3开始即可,因为源头上已经去除了所有的偶数*/; j <= sqrt(i); j++)//这里的sqrt是一个库函数,用于计算平方根,头文件是<math.h>{if (i % j == 0){flag = 0;//当i不是素数时把j改为0break;}}if (1 == flag)//由于i没有被改成0,说明i是素数{count++;//输出素数printf("%d ", i);}}//输出个数printf("\ncount = %d\n", count);return 0;
}

但是这段代码仍然有很大的优化空间。比方说,判断101是不是素数,真的需要拿2,3,4,5,6,7,8,9,10都去试除吗?显然无需这么麻烦。比如说4,6,8什么的就没必要了,也就是说,只需拿奇数去试除,偶数都不需要。继续修改代码

#include <stdio.h>
#include <math.h>//打印100~200之间的素数,并统计个数
int main()
{int i = 0;int count = 0;//统计个数//产生100~200之间的整数for (i = 101; i < 200; i += 2)//偶数肯定不是素数,所以只需产生100~200之间的奇数{int flag = 1;//假设i是素数//判断i是不是素数//拿2~i-1的数去试除i,如果整除就不是素数,如果都不整除就是素数//但是事实上只需要拿2~i的算术平方根之间的数去试除i//产生2~i的算数平方根int j = 0;for (j = 3/*其实这里从3开始即可,因为源头上已经去除了所有的偶数*/; j <= sqrt(i); j += 2/*这里偶数都不需要去试除*/)//这里的sqrt是一个库函数,用于计算平方根,头文件是<math.h>{if (i % j == 0){flag = 0;//当i不是素数时把j改为0break;}}if (1 == flag)//由于i没有被改成0,说明i是素数{count++;//输出素数printf("%d ", i);}}//输出个数printf("\ncount = %d\n", count);return 0;
}

但是本质上,如果把一个合数拆分成几个因子,是可以完全拆分成质数因子的,也就是说,只需要拿质数来试除就行了。但是这样的话,这段代码就要大幅度修改了。篇幅有限,再加上码字有点累了,就暂且到这里吧。

最后说一下,试除法是质数判断方法中最简单、最基础同时效率也最低的一种方法,其他方法诸如筛选法,会从本质上大幅提升效率。这些我会在后面的博客中介绍。

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

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

相关文章

软件测试人需要掌握的测试知识架构体系(上)

软件计划与可行性研究&#xff08;问题定义、可行性研究&#xff09;&#xff1b;需求分析&#xff1b;软件设计&#xff08;概要设计、详细设计&#xff09;&#xff1b;编码&#xff1b;软件测试&#xff1b;运行与维护。 一、软件的生命周期(SDLC) 1、生存周期划分 各阶段…

软件测试/测试开发丨Selenium 高级定位 Xpath

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接&#xff1a;https://ceshiren.com/t/topic/27036 一、xpath 基本概念 XPATH是一门在XML文档中查找信息的语言 XPATH使用路径表达式在XML文档中进行导航 XPATH的应用非常广泛&#xff0c;可以用于UI自…

RT-Thread 线程管理(一)

线程管理 在日常生活中&#xff0c;要完成一个大任务&#xff0c;一般会将它分解成多个简单、容易解决的小问题&#xff0c;小问题逐个被解决&#xff0c;大问题也就随之解决了。 在多线程操作系统中&#xff0c;也同样需要开发人员把一个复杂的应用分解成多个小的、可调度的…

Ubantu安装mongodb,开启远程访问和认证

最近因为项目原因需要在阿里云服务器上部署MongoDB&#xff0c;操作系统为Ubuntu&#xff0c;网上查阅了一些资料&#xff0c;特此记录一下步骤。 1.运行apt-get install mongodb命令安装MongoDB服务&#xff08;如果提示找不到该package&#xff0c;说明apt-get的资源库版本比…

android 实现本地一键打包,告别繁琐的studio操作

前言 在实际开发项目中&#xff0c;我们的工程目录往往是多个app在一个工程下的&#xff0c;每次打包都需要手动的用studio点击Build->Generate Signed Bundle or APK->APK 选择app&#xff0c;签名等&#xff0c;甚至有的app签名还不一样&#xff0c;还需要手动的来回切…

iTOP-RK3588开发板Android12 设置系统默认不休眠

修改文件&#xff1a; device/rockchip/rk3588/overlay/frameworks/base/packages/SettingsProvider/res/values/defaults. xml 文件&#xff0c;如下图所示&#xff1a; - <integer name"def_screen_off_timeout">60000</integer> <integer name&q…

设计模式(一)

1、适配器模式 &#xff08;1&#xff09;概述 适配器中有一个适配器包装类Adapter&#xff0c;其包装的对象为适配者Adaptee&#xff0c;适配器作用就是将客户端请求转化为调用适配者中的接口&#xff1b;当调用适配器中的方法时&#xff0c;适配器内部会调用适配者类的方法…

【【萌新的STM32学习25--- USART寄存器的介绍】】

萌新的STM32学习25- USART寄存器的介绍 STM32–USART寄存器介绍&#xff08;F1&#xff09; 控制寄存器1 &#xff08;CR1&#xff09; 位13&#xff1a; 使能USART UE 0&#xff1a; USART分频器和输出被禁止 1&#xff1a; USART模块使能 位12 &#xff1a; 配置8个数据位…

分类算法系列③:模型选择与调优 (Facebook签到位置预测)

目录 模型选择与调优 1、介绍 模型选择&#xff08;Model Selection&#xff09;&#xff1a; 调优&#xff08;Hyperparameter Tuning&#xff09;&#xff1a; 本章重点 2、交叉验证 介绍 为什么需要交叉验证 数据处理 3、⭐超参数搜索-网格搜索(Grid Search) 介绍…

Maven报错 [ERROR] Malformed \uxxxx encoding.

IDEA刷新项目&#xff0c;报错[ERROR] Malformed \uxxxx encoding. 现象 1.控制台报错 [ERROR] Malformed \uxxxx encoding.2.项目代码大部分爆红 3.Pom文件不爆红 4.IDEA未能构建Dependencies 尝试清除IDEA缓存无效&#xff0c;重新克隆项目无效&#xff0c;更换低版本mav…

Vue2里监听localstorage里值的变化

有的时候,我们需要根据本地缓存在localstorage里值的变化做出相应的操作,这就需要我们监听localstorage: 首先,我们在src下的libs文件夹下新建一个stroage.js用于重写setItem事件,当使用setItem的时候,触发,window.dispatchEvent派发事件 const Stroage = {// 重写set…

考生作弊行为分析算法

考生作弊行为分析系统利用pythonyolo系列网络模型算法框架&#xff0c;考生作弊行为分析算法利用图像处理和智能算法对考生的行为进行分析和识别&#xff0c;经过算法服务器的复杂计算和逻辑判断&#xff0c;算法将根据考生行为的特征和规律&#xff0c;判定是否存在作弊行为。…

Git基础教程-常用命令整理:学会Git使用方法和错误解决

目录 一、了解Git的基本概念 二、Git的安装和配置 Git的安装 Git的配置 用户信息 文本编辑器 差异分析工具 查看配置信息 三、Git的基本操作 基本原理 基本操作命令 基本操作示例 场景一&#xff1a;创建新仓库 场景二&#xff1a;拉取并编辑远程仓库 四、常见问…

开源PHP 代挂机源码,可对接QQ、网易云、哔哩哔哩、QQ空间、等级加速等等

本程序运行环境PHP5.6 95dg/config.php修改系统数据库 进入数据库绑定 你搭建的域名即可 部署完成 进入数据库 找到data 输入绑定授权域名即可进行授权打开此网站 网站是无对接接口 需要您自行找对接接口即可 本源码有点乱 有实力的铁铁 可以修改一下哦&#xff01;

Spring MVC介绍

MVC模式是什么 MVC 模式&#xff0c;全称为 Model-View-Controller&#xff08;模型-视图-控制器&#xff09;模式&#xff0c;它是一种软件架构模式&#xff0c;其目标是将软件的用户界面&#xff08;即前台页面&#xff09;和业务逻辑分离&#xff0c;使代码具有更高的可扩展…

造测试数据

对应sql&#xff1a; from openpyxl import Workbook from faker import Faker# 创建一个Workbook对象 workbook Workbook() # 获取默认的活动工作表 sheet workbook.active# 创建一个Faker对象 fake Faker()# 写入表头 header [Name, Address, Email] sheet.append(heade…

ChatGPT 与 Python进行动态可视化分析

Python数据分析目前最为热门的岗位操作。 想使用Python进行可视化分析,但是又不想写代码,测试,验证。可以交给ChatGPT,open AI 来进行操作。 这样的动态图显示,我们只需要给ChatGPT发送一个指令,人工智能就能很快的实现这一操作。 请使用Python与Echarts做一个动态可视…

【LeetCode】3. 无重复字符的最长子串

3. 无重复字符的最长子串&#xff08;中等&#xff09; 方法&#xff1a;滑动窗口 哈希表 思路 这道题主要用到思路是&#xff1a;滑动窗口 什么是滑动窗口&#xff1f; 其实就是一个队列,比如例题中的 abcabcbb&#xff0c;进入这个队列&#xff08;窗口&#xff09;为 ab…

Vue安装过程的困惑解答——nodejs和vue关系、vue的项目结构

文章目录 一、为什么在使用vue前要下载nodejs&#xff1f;二、为什么安装nodejs后就能使用NPM包管理工具&#xff1f;三、为什么是V8引擎并且使用C实现&#xff1f;四、为什么会安装淘宝镜像&#xff1f;五、什么是webpack模板&#xff0c;为什么需要他&#xff1f;六、vue项目…

GIT命令只会抄却不理解?看完原理才能事半功倍!

系列文章目录 手把手教你安装Git&#xff0c;萌新迈向专业的必备一步 GIT命令只会抄却不理解&#xff1f;看完原理才能事半功倍&#xff01; 系列文章目录一、Git 的特征1. 文件系统2. 分布式 二、GIT的术语1. 区域术语2. 名词术语1. 提交对象2. 分支3. HEAD4. 标签&#xff0…