【算法】牛顿迭代法求平方根的原理和误差分析

article/2025/10/1 8:13:09

前言    

        在《算法(第四版)》中的P23页,给出了经典的利用牛顿迭代法求平方根的算法,牛顿迭代法在数值计算中应用十分广泛,但是在看书中的代码时,我最困惑的是其中对收敛条件的判断,经过查阅资料和论坛,找到了一个自己感觉比较合理的解释,下文主要就简单介绍一下牛顿迭代法和对其在《算法》这本书中的收敛条件设置的理解。

一、牛顿迭代法求平方根原理

public static double sqrt(double c)
{if(c>0) return Double.NaN;double err = 1e-15;double t = c;while (Math.abs(t-c/t) > err * t)t = (c/t + t) / 2.0;return t;
}

        书中的源代码如上所示,在这段简洁巧妙的代码中我们主要理解两个点:

        (1)t = (c/t + t) / 2.0 的由来;

        (2)Math.abs(t-c/t) > err * t 条件的由来;

        首先我们来简单推导一下第一条公式的由来。牛顿迭代法的思想就是在一条曲线上从某一点的切线开始,首先求其与横轴的交点,之后再确定曲线上和该交点横坐标相同的点,并重复求该点的切线与横坐标的交点的方式,不断逼近真实解的过程。网上相关的讲解很多,我这里就简单总结一下牛顿迭代法的步骤:

        1.确定我们需要求解的函数y=f(x),在求平方根中该函数为f(x)=x^2-c;

        2.假设给定初始点的横坐标为x0,则其对应的切线方程为Q(x)=f '(x0)*(x-x0) + f(x0),在求平方根的算法中该切线方程为Q(x) = 2*x0*(x - x0) + x0^2-c;

        3.根据切线方程与横坐标的交点得到下一个迭代点的横坐标,若前一迭代点的横坐标记为Xn,则下一迭代点的横坐标记为Xn+1,令第二条中的x0=Xn,Q(x)  = 0可以得到Xn+1的表达式:

        Xn+1 = (c/Xn + Xn) / 2

        上式便是算法源代码中使用的迭代公式。

二、收敛条件

        在第一部分我们简单介绍了牛顿迭代法求平方根的原理,那么我们再回头看一看源代码中还需要值得我们思考的地方,一个是输入为0的情况,二是判断迭代结束(收敛)的条件

        对于第一个问题我们通过运行代码可以得到以下结果(我自己用C#进行了测试,添加了输出):

         可以看到程序输出了正确的结果,但是其中我们计算的收敛条件和误差分别为+NaN和+0,关于这里就需要对计算机中对浮点数的表示有一定了解,由于篇幅原因大家可以自己查阅IEEE 754标准(CSAPP这本书中有比较详细的解释).

        对于第二个问题,我在网上看到的大部分文章都对Math.Abs(t - c/t) > err*t 这个条件一笔带过,但是这里却是整个算法中最令我困惑的部分,下面给出我的思考:

        首先对于收敛条件,或者说误差的判断条件我们有以下几个选择:

        1.Math.Abs(t*t - c) > err : 最为直观的误差形式,直接带入方程得到与所求函数值的差值,我姑且在这里称其为“绝对误差”,我在一些网上的博文中看到了使用这个误差的代码;

        2.Math.Abs(t - c/t) > err :我又根据数学中对牛顿迭代误差的分析,通过微分中值定理得到形式类似的误差形式,我在这里姑且称之为“中值误差”,这种误差我在stackOverflow上看到了类似代码;

        3.Math.Abs(t - c/t) > err*t :最后就是《算法》这本书中源代码中使用的误差表达形式,这里姑且称之为“算法误差”,如果我没有看过源代码,我自己是不能直接写出这种形式的,那么使用这种形式的误差的理由是什么呢?

        这里我们使用这三种误差来计算1e-100的平方根,代码中的err=1e-15。

        首先是使用“绝对误差”,得到结果如下:

        显然这是个错误答案,因为用IEEE 754标准表示的Double类型其范围为+/-1.7976931348623157E+308,绝对误差一是超出了double的精度范围,二是直接小于我们设定的收敛条件得到了错误答案。

        其次使用“中值误差”,得到结果如下:

        可以看到中值误差也过早地进入到了我们的收敛条件中,得到了错误的结果,那如果我们给err进行一个适应性的缩放会不会得到正确的结果呢?

        最后我们使用《算法》中的源代码进行计算,可以得到:

        可以看到我们得到了正确的结果,通过这个测试我们知道了几点:一是《算法》源代码中的误差形式可以从微分中值定理推导得到,二是为了使算法对于任意的double类型变量都能够得到正确的结果,要对收敛条件进行一个适当的缩放,这样可以避免收敛条件过大导致对于较小的数值得到错误的结果,或对于过大的数值导致收敛过慢或不收敛。

三、小结

        通过这样一个简单的例子,我们发现在进行算法设计中,要关注变量的数值表示和精度范围,用纯数学解析的思想往往会导致在算法中出现难以察觉的错误。


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

相关文章

使用迭代法来求a的平方根

今天朋友问我一道使用迭代法求a的平方根的题,感觉受益匪浅,与诸君相分享 首先我们来看一下题目 我们也无需了解迭代法是什么原理,根据这个题目可以分析得到,需要使用while循环,下面是我的代码实践 #define _CRT_SEC…

利用牛顿迭代法求平方根

求n的平方根,先假设一猜测值X0 1,然后根据以下公式求出X1,再将X1代入公式右边,继续求出X2…通过有效次迭代后即可求出n的平方根,Xk1 先让我们来验证下这个巧妙的方法准确性,来算下2的平方根 (Computed b…

牛顿迭代法求解平方根

一个实例迭代简介牛顿迭代法 牛顿迭代法简介简单推导泰勒公式推导延伸与应用 一个实例 //java实现的sqrt类和方法 public class sqrt {public static double sqrt(double n){if (n<0) return Double.NaN;double err 1e-15;double t n;while (Math.abs(t - n/t) > err…

牛顿迭代法求一个数的平方根(python)

# !/usr/bin/env python # -*- coding: utf-8 -*- """ Author: P♂boy License: (C) Copyright 2013-2017, Node Supply Chain Manager Corporation Limited. Contact: 17647361832163.com Software: Pycharm File: sqrt.py.py Time: 2018/11/19 16:22 Desc:牛顿…

c++迭代法求平方根

用迭代法求x &#xff0c;要求前后两次求出的x的差的绝对值小于10-5&#xff0c;求平方根的迭代公式为&#xff1a; 输入 一个数a。 输出 的值。 样例输入 2.0样例输出 The square root of 2.00 is 1.41421 这道题我无语了...&#xff1a; #include<bits/stdc.h> …

牛顿迭代法求平方根原理

牛顿迭代法可以求解n次方的根&#xff0c;但这里只讨论用它来求平方根。 牛顿迭代法求平方根过程 Java代码实现 /*** 求一个数的平方根* param number* return*/public static double squareRoot(double number){if(number < 0){ //小于0的数无法开平方return Double.NaN;}e…

【算法】牛顿迭代法求平方根及多次方根

1. 概述 牛顿迭代法 牛顿迭代法 牛顿迭代法 是非常高效的求解方程的根的方法。其求解原理可以参考各文献。大体的思路如下&#xff1a; 通过不断地做切线来逼近真实的根&#xff0c;直到误差小于精度。 可得迭代公式&#xff1a; x n 1 x n − f ( x n ) f ′ ( x n ) x_{…

二分法和牛顿迭代法求平方根(Python实现)

求一个数的平方根函数sqrt(int num) ,在大多数语言中都提供实现。那么要求一个数的平方根&#xff0c;是怎么实现的呢&#xff1f; 实际上求平方根的算法方法主要有两种&#xff1a;二分法(binary search)和牛顿迭代法(Newton iteration) 1&#xff1a;二分法 求根号5 a:折半…

牛顿迭代法-求平方根

牛顿迭代法 求出根号a的近似值&#xff1a;首先随便猜一个近似值x&#xff0c;然后不断令x等于x和a/x的平均数&#xff0c;迭代个六七次后x的值就已经相当精确了。 例如&#xff0c;我想求根号2等于多少。假如我猜测的结果为4&#xff0c;虽然错的离谱&#xff0c;但你可以看到…

迭代法求平方根-C

迭代法求a的平方根的近似值&#xff0c;计算公式如下&#xff1a; 迭代是重复反馈过程的活动&#xff0c;目的是为了逼近所需目标或结果。每次对过程的重复称为一次“迭代”&#xff0c;而每一次迭代得到的结果会作为下一次迭代的初始值。 算法&#xff1a;先给定一个假设的平…

用迭代法求某数a的平方根

今天晚上笔试题目最后一题很简单&#xff0c;可是自己做不出 &#xff0c;就是不用库函数&#xff0c;求一个浮点数的平方根。 立马想到用物理法&#xff0c;比如正方形的面积法等&#xff0c;可是求解出不出&#xff0c;然后就绕在里面了。归根到底还是平时的知识储备太少了&…

Java基础——运行时异常和非运行时异常

文章目录 Java中异常机制的体系结构Error&#xff08;错误&#xff09;Exception&#xff08;异常&#xff09;运行时异常和非运行时异常的区别结束 Java中异常机制的体系结构 在Java中&#xff0c;万物皆对象&#xff0c;异常也不例外。 Exception&#xff08;异常&#xff0…

Java编译时异常与运行时异常的区别

Java的异常可以分为编译异常和运行异常&#xff0c;其主要区别&#xff1a; 编译异常要求程序员必须处理&#xff08;捕获或者抛出&#xff09;&#xff0c;不然没法通过编译。 而运行异常可以不处理。 这应该是纸面最明显的区别了&#xff0c;我认为更重要的区别是在处理机…

运行时异常与非运行时异常有什么区别?

运行时异常与非运行时异常有什么区别&#xff1f; 运行时异常 RuntimeException 又称为非检查异常 uncheck exception。是 Exception 的子类。 在 Java 中&#xff0c;异常可以分为两种。Error 和 Exception&#xff0c;它们的父类是 Throwable。 Error 一些底层的类出错&…

杂谈——运行时异常和普通异常有什么区别

说到异常&#xff0c;大家都熟悉&#xff0c;只要程序出错了&#xff0c;那么肯定会说&#xff1a;“哎呀&#xff0c;我的程序出错啦~它抛出异常啦”。 但单单以“异常”的名称来称呼它们&#xff0c;未免也太粗糙了。我们毕竟是一个精致的程序员&#xff0c;当然得知道他们到…

常见的编译时异常和运行时异常

常见的编译时异常和运行是异常 1、粉红色的是编译时异常 2、绿色的异常是运行时异常 3、声明为Error的&#xff0c;则属于严重错误&#xff0c;如系统崩溃、虚拟机错误、动态链接失败等&#xff0c;这些错误无法恢复或者不可能捕捉&#xff0c;将导致应用程序中断&#xff0c;…

浅谈Java异常及其编译时异常和运行时异常的区别

异常是程序编码和运行时经常发生的事件&#xff0c;了解异常有助于我们提高代码质量&#xff0c;增强系统的健壮性&#xff0c;这里总结一下Java编程中的异常、以及Java编译时异常和运行时异常的区别&#xff0c;并列举几种常见的异常&#xff0c;以供参考学习。 一、什么是异…

Java 运行时异常和非运行时异常

异常类型分为两类&#xff1a;运行时异常和非运行时异常。 一、运行时异常&#xff1a; 运行时异常&#xff08;RuntimeException&#xff09;&#xff0c;一般不需要程序员进行捕获。 例如&#xff1a;NullPointException&#xff0c;IndexOutOfBoundsException。如果不对该…

Java-异常处理(编译时异常、运行时异常及处理机制,自定义异常)

个人简介 大家好&#xff0c;我是翰慧腾。一名正在努力学JAVA的大一小白&#xff0c;本文章为初学的笔记&#xff0c;希望各位多多指教。&#x1f499;欢迎点赞收藏留言&#x1f49c;你要批评指点四周风景&#xff0c;首先你要爬上屋顶&#x1f9e1; 一、异常 概述&#xff1a…

通俗理解运行时异常和非运行时异常(一般异常)

一&#xff0c;异常的概念 Java异常类层次结构图&#xff1a; Throwable&#xff1a; 有两个重要的子类&#xff1a;Exception&#xff08;异常&#xff09;和 Error&#xff08;错误&#xff09;&#xff0c;二者都是 Java 异常处理的重要子类&#xff0c;各自都包含大量子类…