寻找完全平方数——浮点数陷阱

【题目描述】

输出所有形如aabb的4位完全平方数(即前两位数字相等,后两位数字也相等)。

【解析】

一、问题分析

从问题出发,题目要求输出的是满足一定条件的数。数在计算机中是要占存储空间的,要在计算机中表示一个数,一定要考虑它的数据类型、数据长度。这里是4位整数,所以用int数据类型就可以了。

如果把数看成一个对象,它也有自己的属性和方法。

二、已知条件分析

1. 已知条件转化为隐含条件。

本题对要输出的数给定了3个已知条件:

(1) 形如aabb(即前两位数字相等,后两位数字也相等)。注意括号内对aabb的解释,只要求前两位相等,后两位也相等,对ab是否相等未作要求。题目未作要求,就是没有限制,就是怎样都可以。所以不要看写着形如aabb就想当然地以为a、b要是不同的两个数。a、b只是两个字母,是未知数,在未给定条件时,它可以是任意数。

(2) 4位数。一个数是几位数是从最左侧的第一个非0数开始计算,比如0123既不是4位数,也不是3位数,因为它根本不是数。所以本题要求是4位数,a的值不能为0。也就是a的取值范围是1-9,b的取值范围是0-9。

(3)完全平方数。完全平方数(perfect square)指能表示成某个整数的平方的数。例如,0、1、4、9都是完全平方数,因为它们分别是0、1、2、3的平方。根据定义可知,完全平方数是非负整数。

2. 隐含条件转换为数学表达式。

仅仅将已知条件转化为隐含条件还不够,还要进一步转化为数学表达式,才可能将其转化为代码。

上述三个条件中主要涉及的数学表达式是aabb这个数表示,它可以表示成:a*1100 + b*11

三、算法分析

本题的本质是从一定范围的整数中找出一些符合特殊条件的整数,所以可以用“穷举法”。

穷举法的穷举范围极大地影响程序的效率,所以要尽量缩小穷举的范围。

本题的穷举范围和符合条件判断有两种:

①大范围:4位数→缩小范围:4位数+形如aabb的数(无法进一步缩小),符合条件:完全平方数。

②大范围:4位数→缩小范围:4位数+完全平方数(无法进一步缩小),符合条件:形如aabb

下面分别阐述两种算法。

1. 遍历范围:4位形如aabb的数,符合条件:完全平方数

这个4位数用aabb表示,前面讨论过,a的取值范围是1-9,b的取值范围是0-9。也就是说a取的每一位数字,都要与b取的0-9组合一遍,就像相亲节目中每个男生都会用目光欻欻歘歘朝着每个女生闪烁一遍。每个男生一个接一个上台轮流闪烁,这是一次循环;每个男生上台后闪烁对面一个接一个的女生,又是一次循环。所以程序要用到循环的嵌套:循环里面套循环。

程序主干如下:

for(int a = 1; a <= 9; a++)for(int b = 0; b <= 9; b++)if(aabb是完全平方数) printf("%d\n", aabb);

这段主干程序并不是合法的C程序,第三行代码是不符合语法规范的,因而无法运行。

这样的不能真正运行的简化代码称为伪代码(pseudocode)。伪代码主要用于描述算法梗概,避开细节,启发思路。在使用伪代码时,可以不必拘泥格式,只求简明即可。

这段伪代码完整地表现出了程序的三个部分:遍历、判断、输出。

上述伪代码要变成真正的代码,需要解决两个问题:

(1) aabb的表示。

C语言中是无法用aabb这样的形式输出数的,因为它会被编译器识别为变量。把伪代码改写成代码时,一般先选择较为容易的任务来完成。前面讲过,这个问题比较简单,只要用a*1100 + b*11这种表达式的形式表示即可。

(2) 判断aabb是否为完全平方数。

这还不简单吗?只要用sqrt()函数给aabb开平方,再判断结果是不是一个整数不就行了?

这是正向思维,这个思路的难点在于,sqrt()函数的返回值是double类型,这种浮点数是有误差的。这意味着如果它的返回值是0.9999999999,它的真实值可能是一个整数1。

所以咱们是不能用下面这种方式来进行判断的:

if(sqrt(aabb)==最接近sqrt(aabb)的整数)printf("%d\n", aabb);

要判断一个浮点数是不是整数,只能用一个不太完美的办法:求这个浮点数与最接近它的值的整数的差,如果这个差小于一个很小的数,就认为它是整数。

那这个很小的数应该是多大合适呢?

系统为咱们定义了名叫DBL_EPSILON的宏,定义在头文件<float.h>中。它就是那个很小的正数,用于处理浮点数精度问题。DBL是double的缩写。EPSILON是希腊语,代表第五个希腊字母ε。ε在数学中常被用来表示一个非常小的正数,这个数可以任意小,但不等于零。

代码如下:

#include<stdio.h>#include<math.h>#include <float.h>int main(){for(int a = 1; a <= 9; a++)for(int b = 0; b <= 9; b++){int aabb = a*1100 + b*11; //这里才开始使用n,因此在这里定义ndouble r1 =  sqrt(aabb);int r2 = floor(r1 + 0.5);if(fabs(r1 - r2) < DBL_EPSILON) printf("%d\n", aabb);}return 0;}

注意sqrt(aabb)后面加上0.5这个细节,这是为了在转化为整数时进行四舍五入。函数floor(x)返回不超过x的最大整数。这里floor(sqrt(aabb) + 0.5)其实也可以写成:int r = (int)(sqrt(aabb) + 0.5)。后者是用数据类型强制转换的方式,舍去小数部分。它和用floor函数有什么区别呢?读者可以猜想一下。

fabs(x)返回x的绝对值。

虽然用上述代码也能输出正确的结果,但不能改变它是个天生残疾的事实。而且如果你忘了或不知道那个很小的数的宏名更当如何呢?

换一种思路,用逆向思维,就能避开浮点数误差,找到完美的算法:求出最接近sqrt(aabb)的整数,反过来判断它的平方是否等于aabb。

代码如下:

#include<stdio.h>#include<math.h>int main(){for(int a = 1; a <= 9; a++)for(int b = 0; b <= 9; b++){int aabb = a*1100 + b*11; //这里才开始使用n,因此在这里定义nint r = floor(sqrt(aabb) + 0.5);if(r*r == aabb) printf("%d\n", aabb);}return 0;}

2. 遍历范围:4位完全平方数,符合条件:形如aabb

这种方法天生具有完美基因,因为它不涉及浮点数。

整数开方会产生小数,整数的平方只能是整数。

#include<stdio.h>int main(){for(int x = 1; ; x++){int aabb = x * x;if(aabb < 1000) continue;if(aabb > 9999) break;int hi = aabb / 100;int lo = aabb % 100;if(hi/10 == hi%10 && lo/10 == lo%10) printf("%d\n", aabb);}return 0;}

continue和break语句是C语言的两个语句。continue的作用是跳过当前循环体中剩余未执行的语句,并立即开始下一次的循环条件判定,即执行下一次循环。break的作用是直接跳出循环,即立即结束整个循环。

两种算法都介绍完了,问题来了,哪个算法效率更高呢?你猜猜!

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

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

相关文章

力扣---接雨水---单调队列

题目&#xff1a; 单调队列思想&#xff1a; 没有思路的小伙伴可以先把这个想清楚哦&#xff1a;力扣hot10---大根堆双端队列-CSDN博客 从上面的图就可以发现&#xff0c;如果柱子呈递减序列&#xff0c;那么不会接到雨水&#xff0c;只要有一个小凸起柱子&#xff0c;那么这个…

变换,动画

面试题——需求&#xff1a;在不知道父元素与子元素的宽高时 如何让子元素在父元素内居中&#xff1f; 1.定位 父相子绝 2.子元素 top&#xff1a;50% left:50% 3.子元素 transform: translate(-50%,-50%) .parent{height: 500px;background-color: red;position: relative;}.c…

2024年新手视频剪辑软件推荐-6款视频剪辑软件测评

视频剪辑软件推荐 premiere premiere 直达地址:各大软件网站 说到底,还是得专业的来,虽然很多人觉得他是收费的,但是你懂的,想要免费总是会有办法的.别的不说,剪辑这块,我还是很认可这个软件,虽然我现在还是刚入门. 剪映 剪映 抖音官方推出的一款手机视频编辑剪辑应用,提供切割…

回顾 CESS 在 ETHDenver:2024,数据价值网络之年

ETHDenver 2024 是全球最大的 Web3 BUIDLathon 活动之一&#xff0c;吸引了来自各地的区块链爱好者、开发者和建设者。作为一个汇聚了全球的技术爱好者和创新者的社区驱动创新节&#xff0c;ETHDenver 不断推动区块链技术、文化和教育的融合发展。今年的活动已聚集数万名社区成…

基于极大似然算法的系统参数辨识matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于极大似然算法的系统参数辨识。对系统的参数a1&#xff0c;b1&#xff0c;a2&#xff0c;b2分别进行估计&#xff0c;计算估计误差以及估计收敛曲线&#xff0…

设计模式 代理模式

代理模式主要使用了 Java 的多态&#xff0c;主要是接口 干活的是被代理类&#xff0c;代理类主要是接活&#xff0c; 你让我干活&#xff0c;好&#xff0c;我交给幕后的类去干&#xff0c;你满意就成&#xff0c;那怎么知道被代理类能不能干呢&#xff1f; 同根就成&#xff…

光网络:SONET、SDH、DWDM的区别

SONET&#xff08;Synchronous Optical Network&#xff09;、SDH&#xff08;Synchronous Digital Hierarchy&#xff09;和DWDM&#xff08;Dense Wavelength Division Multiplexing&#xff09;都是在光纤通信领域中使用的关键技术。它们在提供高容量、高效率、可靠性等方面…

【Tauri】(4):使用Tauri1.5版本+candle框架运行大模型,前后的搭建运行成功,整合前端项目,在应用中显示。

1&#xff0c;视频地址 关于tauri 框架 2&#xff0c;搭建rust 环境 # 设置服务器国内代理&#xff1a; export RUSTUP_DIST_SERVER"https://rsproxy.cn" export RUSTUP_UPDATE_ROOT"https://rsproxy.cn/rustup"# 设置环境变量 export RUSTUP_HOME/data/…

sort函数详解

往期文章推荐&#xff1a; [C] 非常实用的知识点-CSDN博客 1.8编程基础之多维数组————14:扫雷游戏地雷数计算-CSDN博客 &#xff08;并不怎么华丽的分割线&#xff09; 前言 话说在C中有这么一类算法&#xff0c;叫做排序算法。 它有许多分支&#xff1a;冒泡排序&a…

基于springboot实现图书推荐系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现图书馆推荐系统演示 摘要 时代的变化速度实在超出人类的所料&#xff0c;21世纪&#xff0c;计算机已经发展到各行各业&#xff0c;各个地区&#xff0c;它的载体媒介-计算机&#xff0c;大众称之为的电脑&#xff0c;是一种特高速的科学仪器&#xff0c;比…

猫头虎分享已解决Bug || 数据中心断电:PowerLoss, DataCenterBlackout

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

帮管客CRM jiliyu接口存在SQL漏洞 附POC软件

免责声明:请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。 1. 帮管客CRM简介 微信公众号搜索:南风漏洞复现文库…