解平方根算法(四种解法+层层深入+逐步简化)

article/2025/9/21 16:12:25
专栏: 学习笔记
同一个问题可能存在多种不同的算法;不同算法的思路不同,在解题的效率上也有很大不同。
对于算法的设计,岂可不慎乎 !

解平方根算法一(趋近法

对于输入一个任意实数 c ,输出c 的算术平方根 g。根据以往的常识,可能最先想到的就是采用趋近的方法来求解。

① 先从0到c的区域里选出一个整数g1估计值,满足g1的平方小于c 并且 g1+1的平方大于c 的条件。
② 以步长h增加g1,即g1=g1+h, 其中h 为设置的精度(下文代码设为0.00001),使其结果不断向精度解靠拢。
#include <iostream>
#include <cmath>
using namespace std;
void square_root(double c)
{int r=0;double g=0;for(int i=0;i<c+1;++i){if(i*i == c){g=i;printf("%.5f",g);}if(i*i > c){g=i-1;break;}}while(abs(g*g -c) > 0.0001){  //判断g^2-c是否在精度范围内g+=0.00001;             //g每次加固定步长,以逼近所求解r+=1;printf("%d : g = %.5f\n",r,g);}}
int main()
{double c;cin>>c;square_root(c);return 0;
}

当我们输入9时它能很快算出3.00000

但是当我们输入10后:

1 : g=3.00001
2 : g=3.00002
.
.
.
16226 : g=3.16226
16227 : g=3.16227    

好家伙,这算法一下就变得非常糟糕了,如果把解的精度和步长缩小,那么运行时间太大了,因此我们必须换一种算法~

解平方根算法二(二分法

二分法的字面意思就是“一分为二”的方法,其基本思想就是,每次将求解值域的区间减少一半,因此可以快速缩小搜索范围。将二分法运用在此题上,就是期望能快速的找到平方根精确值所在的区间。

#include <iostream>
using namespace std;
void square_root(double c){double r=0,g=c;int j=0; //记录循环步数 while(g-r>1e-8){double mid=(g+r)/2;if(mid*mid > c)  g=mid; //说明mid右边 都满足mid*mid>c,所以用g=mid,缩小取值范围 else r=mid; //和上面一样的道理j+=1;printf("%d : %.7f\n",j,mid);}
}
int main()
{double c;cin>>c;square_root(c);return 0;
}

我们这里输入c=10;

结果:

1 : 5.0000000
2 : 2.5000000
3 : 3.7500000
.
.
25 : 3.1622776
26 : 3.1622778
27 : 3.1622777
28 : 3.1622777
29 : 3.1622777
30 : 3.1622777

我们非常成功的将算法一的16227次循环简化到了30次循环,并且还大大提高了精确度

既然算法二已经如此的高效了,这是不是就意味着算法二即为平方根的最佳解法呢?

有趣的是,还可以进一步的修改算法,获得更少的循环次数,加快g 的求解过程,而且还能得到同样精度甚至更高的精度解。在算法中,要养成良好的思维习惯,持续不断的对设计进行优化,寻找更高效的解法,这也是计算机科学之美的重要体现~

解平方根算法三(牛顿迭代法

因为要求c 的平方根,所以满足 x^2=c ,此时x 的解就是c 的平方根,因此不妨令f(x)=x^2-c,设x0为方程的解,即f(x0)=0。选取g0作为x0 的初始近似值,算法的核心在于如何推导下一个点g1 ,使得g1 更趋近于正确值x0. 以此类推直到找到精确范围内的正确解就行了。

算法的思想就是:当x=g0 时,过点f(x) 做一条切线。这条切线与x 轴相交于一点,这个交点就是g1 。

从图1-7 可以清楚地看到g1 比 g0 更趋近于x0。 然后再经过f(g1)做一条切线,同样该切线与x轴的交点成为下一个更趋近于x0 的近似值g2.以此类推就行了~

通过数学计算,可以得出g1 和g0 的关系是 g1=(g0+c / g0 ) / 2

对证明感兴趣的友友可以看一下:

过点(g0, f(g0))做f(x) 的切线 L ,f(x)的导数为2x ,所以 切线L 的斜率就是f '(g0)=2g0 。
然后就可以求L 的切线方程:y= f(g0) + f '(g0)( x - g0 ) ,设L和x 轴的交点坐标为(g1,0),则
0=f( g0 ) +f '( g0 )( g1- g0 ) 。因为f(g0 )= g0^2-c 且 f '(g0)=2g0 。将其带入计算化简,得到:
g0^2 -c + 2g0( g1 - g0 )= 0 ,所以2g0g1=c-g0^2+2g0^2= c+g0^2 ,化简得到:
g1=( g0 + c / g0 ) / 2

以此类推,每次循环都将近似值用这个公式更新,就能快速得到c 的算术平方根~

#include <iostream>
#include <cmath>
using namespace std;
void square_root(double c){int j=0; //记录循环步数 double g=c/2; //先将g设为 区间中点,while(abs(g * g -c) >1e-11){g=(g+c/g)/2;j+=1;printf("%d : %.10f\n",j,g);}
}
int main()
{double c;cin>>c;square_root(c);return 0;
}

我们输入c=10

1 : 3.5000000000
2 : 3.1785714286
3 : 3.1623194222
4 : 3.1622776604
5 : 3.1622776602

我们非常成功的将算法二的30次循环简化至5次循环,并且还大大提高了精确度,岂不美哉 !!

升级版牛顿迭代法

到这里,我们可能会疑惑了,求平方根的 sqrt( )函数的底层算法效率的问题。

这里我也没办法讲清楚,看这篇博客sqrt()函数实现(底层探究)

你会看到这份“神一样”的代码:‘

float Q_rsqrt( float number )
{long i;float x2, y;const float threehalfs = 1.5F;x2 = number * 0.5F;y   = number;i   = * ( long * ) &y;   // evil floating point bit level hackingi   = 0x5f3759df - ( i >> 1 ); // what the fuck?y   = * ( float * ) &i;y   = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration// y   = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed#ifndef Q3_VM#ifdef __linux__assert( !isnan(y) ); // bk010122 - FPE?#endif#endifreturn y;
}  

上述源代码解析: 点这里

当然我只能看出它本质上还是使用的牛顿迭代法,但真正看不懂的是 0x5f3759df86这个神一样的数字的由来,经过这一步计算(上面what the fuck那步),使得这个值非常接近1/sqrt(n),因此我们只需要2次牛顿迭代就可以达到我们所需要的精度,是真的六!!。

精简版:

float InvSqrt(float x)
{float xhalf = 0.5f*x;int i = *(int*)&x; // get bits for floating VALUE i = 0x5f375a86- (i>>1); // gives initial guess y0x = *(float*)&i; // convert bits BACK to floatx = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracyreturn x;
}  
运行效率:趋近法 < 二分法 < 普通牛顿迭代法 < sqrt()函数 < 升级版牛顿迭代法

最后总结:解决同一个问题可以设计出各种不同的算法,不是获得解就结束了,而且要分析不同算法之间对于程序执行效率的影响,不同的算法会有很显著的性能优劣差异,岂可不慎乎!!

References

[1] 《计算机科学导论—以python为舟》 (沙行勉 著)

[2] sqrt函数实现(神奇的算法)


http://chatgpt.dhexx.cn/article/aFk94LcZ.shtml

相关文章

求平方根的几种方式

求平方根的几种方式 前言一、二分法求平方根二、牛顿法求平方根三、不动点法求平方根四、更抽象的方式参考 前言 最近在看神书《SICP》&#xff0c;刚看了第一章&#xff0c;虽然有些难啃&#xff0c;但感觉确实啃得确实“香”。说不上醍醐灌顶&#xff0c;但应该也是受益匪浅了…

[从零开始学算法]求平方根

这次我们来学习一下如何求平方根。在计算机中很难有精确的求出数据的平方根的算法&#xff0c;基本都是要求一个误差可接受范围内的近似值。治理我们取误差值为1e-5。 笔者的编程语言及环境如下 编程语言&#xff1a;c编译器&#xff1a;Code Blocks系统&#xff1a; windows …

word中首字母大写问题处理

和word中的大多数显示问题相关&#xff0c;它的处理也是在文件——选项中处理&#xff0c;具体处理如图所示。

word中输入英文字母的时候,自动首字母大写,如何解决(以WPS为例)

我们在使用word编辑文档的时候&#xff0c;有的时候需要输入英文&#xff0c;但是却出现了输入一个英文&#xff0c;然后再按一下空格键&#xff0c;首字母自动变成大写的情况&#xff0c;那么现在就来学习一下如何解决这个问题 如图所示&#xff0c;输入单词&#xff0c;首字…

Word2003取消首字母大写方法

打开Word2003文档&#xff0c;编辑菜单栏的“工具”下拉列表中的“自动更正选项”&#xff0c;在弹出的“自动更正”窗口中将“句首字母大写”前面的钩取消&#xff0c;然后确定即可 提示&#xff1a;大家可以看到上图中还有表格单元格的首字母大写、英文日期第一个字母大写、更…

C语言编写取单词首字母,C语言练习之单词首字母大写

/* *Copyright(c) 2016,烟台大学计算机学院 *All rights reserved. *作 者&#xff1a;刘金石 *完成日期&#xff1a;2016年4月22日 *版本 号&#xff1a;v1.0 *问题描述&#xff1a;字符串中每个单词首字母变大写 */ #include int main() { int i; int word; char str[200]; …

word首字母不默认大写

这样在word里打一行英文时 首字母就不会默认大写 ------------------------------------------- 1. 2. 3.

word文档开头首字母取消自动检查大写

1、在word中先输入文本 2、打开菜单栏->审阅->拼写和语法 3、打开选项按钮 4、打开自动更正选项 里面可以修改文档中是否检查大写以及表格中是否检查大写。

word文档取消英文首字母大写

word文档取消英文首字母大写 这里我是用的OneNote做为演示&#xff0c;word文档是一样的操作 1、点击文件&#xff0c;再选择选项 2、进入校对&#xff0c;再选择自动更正选择 3、取消句首字母大写&#xff0c;再点击确定

【word基础】如何取消word首字母大写

转载于:https://www.cnblogs.com/xphdbky/p/7604566.html

word2016取消首字母大写 带图详细讲解

文件-->选项-->校对-->自动更正选项-->句首字母大写&#xff08;对勾去掉&#xff09; operation is over.

java首字母变大写_Java 首字母转大写

1.代码实现 /* * 首字母转大写 * attention: * date: 2020年11月17日 0017 14:51 * param: word 待转换字符串 * return: java.lang.String 首字母转成了大写 */ public static String convertInitialUpper(String word) { if (StringUtils.isEmpty(word)) return ""…

c语言将首字母变大写,c语言问题 将首字母变为大写

#include int main() { int i; int word; char str[200]; printf("请输入字符串:"); while(gets(str)!NULL) { printf("修改后的字符串为:"); word0; for(i0;str!\0;i) { if(str) { word0; printf(""); } else if(word0) { word1; strstr-32; pr…

怎么取消和设置Word中首字母大写

①打开Word文档 ②单击Word文档的“ 文件 ”选项卡&#xff08;左上角&#xff09; ③在侧栏中选择“选项” ④单机“编辑” ⑤勾选或放弃勾选就可以对其进行设置(操作完成后不要忘记点击“确定”&#xff09;

取消Word自动首字母大写步骤

前言 有时候在Word中输入英文字符时首字母会自动大写&#xff0c;但是这并不是我们想要的结果&#xff0c;本文是取消首字母大写的步骤。 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a;

world 如何输入英文时取消首字母大写

1.打开Word文档&#xff0c;找到文档左上方的“文件”选项&#xff0c;点击“文件”选项&#xff1b; 2.点击“选项”&#xff0c;找到校对/编辑&#xff1b; 3.“找到自动更正”&#xff0c;并取消“首字符大写”的选项就可以了

去掉wps的word中首字母大写

左上角文件——>选择最下面的“选项”——>选择弹窗中的“编辑”——>把红框中的勾去掉即可

office365 word如何关闭首字母大写

问题 在Office365中无论写什么字母&#xff0c;首字母永远会自动变成大写的&#xff0c;无论你需要不需要&#xff01; 解决 1、打开word&#xff0c;点击【文件】>>【选项】 2、点击【样对】>> 【自动更正选项】 3、根据需要&#xff0c;取消【句首字母大写】和…