求平方根的几种方式

article/2025/9/21 16:10:22

求平方根的几种方式

  • 前言
  • 一、二分法求平方根
  • 二、牛顿法求平方根
  • 三、不动点法求平方根
  • 四、更抽象的方式
  • 参考

前言

在这里插入图片描述

  最近在看神书《SICP》,刚看了第一章,虽然有些难啃,但感觉确实啃得确实“香”。说不上醍醐灌顶,但应该也是受益匪浅了。书中介绍了一些关于计算机数值求解的一些问题,这里抽取一点求平方根的算法,做个总结,希望可以便人便己。


一、二分法求平方根

在这里插入图片描述

二分法大概比较简单的一种求解的方法,它理论基础是零点存在定理。即:
在这里插入图片描述
通过事先确定零点所在的区间范围[a,b](区间范围端点值异号),通过不断缩小这个区间范围,达到逐渐逼近最终解的目的。而缩小区间范围的方法就是取[a,b]的中点m,判断f(m)的正负号与f(a)、f(b)的关系,改变a的值(f(b)*f(m)<0)或者b的值(f(a)*f(m)<0)为m,每次缩减一半的解空间。循环上述操作,直到最终的范围小于所给的精度范围。

上面说的是通用的求解方程根的方法,对于平方根来说,也是适用的。代码如下:

//sqrt with binary search 
//note:l less than h
double sqrtWithBiSear(double num,double l,double h){//define function f(x) = x*x -num;auto difFunc = [num](double x)->double { return x*x - num;};//make sure that there will be one root at leastif(difFunc(l) *difFunc(h) > 0){return -1;}const double LIMIT = 0.00001; while(l<=h){double m = (l+h)/2;double difM = difFunc(m);if(fabs(difM) < LIMIT){return m;}double difL = difFunc(l);double difH = difFunc(h);if(difL* difM <= 0) {h = m;}else if( difH * difM <= 0 ) {l = m;}else {return -1;}}return -1;
}

其实对于单调递增的函数来说还可以简化一点:

//sqrt with binary search 
double sqrtWithBiSear1(double n){double h = max(1.0,n);double l = min(1.0,n);const double LIMIT = 0.00001;while(1){double guess = (l+h)/ 2;double guessSquare = guess*guess;double dif = guessSquare - n;if(fabs(dif) < LIMIT){return guess;}if(dif > 0){h = guess;}else {l = guess;}}return -1;
}

对于二分法求解来说,它的优点是对函数f(x)的要求不太高,只需要其在根所在的范围区间内连续,且在这个范围内提供一个异号的[a,b]; 但是其缺点是不能求偶数重根,也不能求复数根。由于每次砍掉一半的空间,其算法的复杂度是O(lgN)级别的,收敛的速度不算太快。

二、牛顿法求平方根

由于很多方程其实并不存在求根公式,而且很多时候现实中的一些应用也并不需要数学意义上的精确解,为了得到方程的近似零点,牛顿等人提出了使用迭代的方式来求这个近似的零点。

方法的思路总的来说四个字,“以直代曲”,使用切线来代替曲线。如下图所示。
在这里插入图片描述

下图显示了迭代的过程
在这里插入图片描述
通过xk,xk+1,xk+2,… 这种不断迭代的方式最终逼近至x*(当然只需要计算到指定精度的解就行)。

具体的迭代方程可以通过列切线方程,也可以通过泰勒展开式获取,最终获得的迭代方程如下图所示。
在这里插入图片描述

对于具体的求平方根来说,将f(x)=x^2-a=0(a>0)带入上述公式,可得
在这里插入图片描述


有了迭代方程,代码编写就不是问题了。

double sqrtWithNewdonWay(double x, double guess){//define goodEnough? functionauto goodEnough = [](double x, double guess)->bool {const double LIMIT = 0.00001;if(fabs(x - guess*guess) <= LIMIT){return true;}return false;};while(!goodEnough(x, guess)){guess = (guess + x/guess) / 2; }return guess;
}

对于牛顿法来说,它在单根附近的收敛速度较快,算法逻辑也比较简单,而且可以求代数方程的重根、复根。但是牛顿法有个问题,就是其并不是时时收敛的,和具体的函数、选取的初值都有关系。关于收敛性的讨论不在这篇博客的讨论范围之内,有兴趣的童鞋可以查阅其他资料。

三、不动点法求平方根

不动点法求解方程的根,其基本原理是将f(x) = 0转化为 一个等价的x = g(x), 或者说x(n+1) = g(x(n)) 。通过上述方程得到逐渐逼近一个解x0,使得x0 = g(x0)。则f(x0) = 0,即x0是方程f(x)的零点。

上述表述的不明确?下面是从【5】中“偷”的图。
在这里插入图片描述

还不理解?

去看看【5】中的内容吧,里面还有说不动点的几何意义。适合小阅一番。

有一点注意的是,构造出来的迭代函数g(x)不一定收敛,需要满足一定的条件,才会收敛。具体的条件这里也就不说了(因为我也不是很懂╮(╯▽╰)╭),请各位学霸同学自行查阅资料,谢谢。


上面是通用的求解方程根的解法,对于具体的求解平方根来说,可以构造一个和上述牛顿法一样的迭代公式,即
在这里插入图片描述

事实证明,它是收敛的。(如果构造一个g(x) = a/x ,它就不是收敛的)

实现的代码如下:


//fixed points of function
double sqrtWithFixPointMethod(double x,double guess){//define improveGuessauto improveGuess = [](double x,double guess)->double {return (guess+x/guess)/2;       // iterative formula};//define goodEnough? functionauto goodEnough = [](double v1, double v2)->bool {const double LIMIT = 0.00001;if(fabs(v1-v2) < LIMIT) {return true;}return false;};while(!googEnough(guess,nextGuess)){guess = nextGuess;nextGuess = improveGuess(x,guess);}return nextGuess;
}

对于不动点求解方程来说,它的收敛性也是个问题。构造不同的迭代函数g(x)可能会有不同的收敛性,而且要求g(x)在区间[a,b]上的函数值也在此区间内,对给定的初值要求较高。关于不动点法收敛性更详细的讨论可以参考【6】中所述。

四、更抽象的方式

上面是三种求平方根的实现代码,在SICP中还介绍了如何用更抽象的方式来定义函数。这儿也用cpp实现了一部分,供君一览。

//abstract version of binary serach method
double biSearMethod(std::function<double (double)> f,double l, double h){const double LIMIT = 0.00001;while(l<=h){double mid= (l+h)/ 2;double f_m = f(mid);if(fabs(f_m) < LIMIT){return mid;}if( f_m > 0){h = mid;}else {l = mid;}}}

//f is transform function, guess is the first guess value
double fixedPointMethod(std::function<double (double)> f, double guess){// define goodEnoughFunc with lambda expressionauto goodEnoughFunc = [](double v1, double v2) -> bool {const double LIMIT = 0.00001;if(fabs(v1-v2) < LIMIT) {return true;}return false;};// define improveGuessFunc with lambda expressionauto improveGuessFunc = [=](double guess)->double {return f(guess);};double nextGuess = improveGuessFunc(guess);while(!goodEnoughFunc(guess,nextGuess)){guess = nextGuess;nextGuess = improveGuessFunc(guess);}return nextGuess;}
//Derivative functions
std::function<double(double)> deriva(std::function<double(double)> f) {const double dx = 0.00001;return [=](double x)->double { return ((f(x+dx) - f(dx))  / dx);};
}std::function<double(double)> newtonTransfrom(std::function<double(double)> g){return [=](double x)->double { return  x - ( g(x) / (deriva(g)) (x) );     //define:xn+1 = xn - g(x) / d(g(x))};
}double newtonMethod(std::function<double(double)> h,double guess) {return fixedPointMethod(newtonTransfrom(h),guess);
}

参考

【1】《Structure and Interpretation of Computer Programs》
【2】方程求根:二分法–不动点迭代–牛顿法–弦截法
【3】牛顿迭代法求平方根
【4】如何通俗易懂地讲解牛顿迭代法求开方(数值分析)?
【5】不动点迭代法—单变量非线性方程近似根matlab求解
【6】不动点迭代及其收敛性


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

相关文章

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

这次我们来学习一下如何求平方根。在计算机中很难有精确的求出数据的平方根的算法&#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;取消【句首字母大写】和…

WPS中的word如何取消英文首字母大写

不知道你是不是一直也有这样的烦恼&#xff0c;记笔记的时候&#xff0c;特别是有时候敲代码&#xff0c;总是敲完一个单词&#xff0c;摁下空格就直接首字母变成大写了,就像下图一样~~~~ 那我们可以在wps中&#xff0c;先点击左上角的文件按钮。 再往下拉找到选项按钮。 点…