求平方根问题

  • 概述:本文介绍一个古老但是高效的求平方根的算法及其python实现,分析它为什么可以快速求解,并说明它为何就是牛顿迭代法的特例。

  • 问题:求一个正实数的平方根。

    给定正实数 \(m\),如何求其平方根\(\sqrt{m}\)?

    你可能记住了一些完全平方数的平方根,比如\(4, 9, 16, 144, 256\)等。那其它数字(比如\(105.6\))的平方根怎么求呢?

    实际上,正实数的平方根有很多算法可以求。这里介绍一个最早可能是巴比伦人发现的算法,叫做Heron’s algorithm。

算法描述

假设要求的是\(x = \sqrt{m}\),Heron’s 算法是一个迭代方法。

在迭代的第\(k=0\)步,算法随机找一个正数作为\(x\)的初始值,记为\(x_0\),例如\(x_0 = 1\)。在第\(k \in [1, 2, ...]\)步,计算 \(x_{k} = \frac{x_{k-1}}{2} + \frac{m}{2 x_{k-1}}\)。每迭代一步,算法计算\(x_k\)与\(x_{k-1}\)的变化,\(\Delta_k = |x_k - x_{k-1}|\),若 \(\Delta_k < \epsilon\),则算法结束,输出\(x_k\)。

这里\(\epsilon\)是计算精度要求,可以取一个小正数,如\(1e-14\)。算法描述如下:

  1. 初始化 \(x = 1\);
  2. 计算 \(\Delta_k = |x_k - x_{k-1}|\);
  3. 若\(\Delta_k < \epsilon\),返回\(x\),算法结束;
  4. \(x \leftarrow \frac{x}{2} + \frac{m}{2 x}\);
  5. 返回第2步;

在 \(m\)比较大时,可能会出现迭代次数增多,数值不稳定的情况。我们可以通过将\(m\)缩放到一个较小的区间,求缩放后的平方根,然后再反缩放得到\(m\)的平方根。

考虑任意一个正实数 \(m\),我们总是可以将\(m\)看作两个正实数的乘积 \(m = n * 4^u\),其中\(n \in [0.5, 2]\)。此时有 \(\sqrt{m} = \sqrt{n} * \sqrt{2^{2u}} = 2^u * \sqrt{n}\)。因此只需要计算 \(\sqrt{n}\),计算结果乘以\(2^u\),就可以得到 \(\sqrt{m} = 2^u *\sqrt{n}\)。

由于\(n \in [0.5, 2]\),求根过程更容易,误差也可以得到较好的控制。

这里的\(u\)是将\(m\)连续\(u\)次除以4,或者将\(m\)连续\(u\)次乘以4来缩放到指定的\([0.5, 2]\)区间:

  1. 初始化 \(u=0\);
  2. 若 \(0.5 \leq m \leq 2\),返回 \(u\),算法结束;
  3. 若 \(m > 2\): \(m \leftarrow m / 4\), \(u \leftarrow u+1\);
  4. 若 \(m < 1\): \(m \leftarrow m * 4\), \(u \leftarrow u-1\);
  5. 返回第2步;

代码实现

"""
Heron's 求平方根算法
@data_algorithms
""" def heron(m, epsilon=1e-15):
"""
Heron's 求根算法
@m: 带求根的正实数
@epsilon: 迭代结束条件
@return: m的平方根
"""
if m <= 0:
raise ValueError("Non-negative real numbers only.")
m, u = scale(m) #m缩放到[0.5, 2]区间
x = 1
delta = abs(x)
while delta >= epsilon:
newx = 0.5 * (x + m / x)
delta = abs(newx - x)
x = newx
return x * (2 ** u) def scale(m, base=4):
"""
@m: 正实数m
@base: m = base ^ u + m~
@return: m~, u
"""
u = 0
while m > 2:
m = m / 4
u += 1
while m < 0.5:
m = m * 4
u -= 1
return m, u def heron_test():
from math import sqrt
from random import random
epsilon = 1e-15
for _ in range(100000):
m = random()* 1e10
error = abs(heron(m) - sqrt(m))
assert error < 1e-10 if __name__ == "__main__":
from math import sqrt
for x in [105.6, 0.1, 0.5, 1.5, 2,
4, 10, 123, 256, 1234]:
print(x, heron(x), sqrt(x))
heron_test()

算法分析

为什么Heron’s 算法能够快速找到平方根呢?

我们可以通过观察每一步迭代的误差来进行分析。

假设要求解的真值为 \(x\),即\(x=\sqrt{m} \Rightarrow m = x^2 = m\)。

在第\(k\)步,算法的误差是\(e_k = (x_k - x)\)。在第\(k+1\)步,\(x_{k+1} = \frac{x_{k}}{2} + \frac{m}{2 x_{k}}\), 其误差是 $e_{k+1} = (x_{k+1} - x) =(\frac{x_{k}}{2} + \frac{m}{2 x_{k}} - x) = (\frac{x_k - x}{2} + \frac{m - x_k x}{2x_k}) =(\frac{e_k}{2} - \frac{x(x_k - x)}{2x_k}) = (\frac{e_k}{2} - \frac{xe_k}{2x_k}) = \frac{e_k^2}{2x_k} $。所以 \(e_{k+1} = \frac{e_k^2}{2x_k}\),即后一次迭代的误差与前一次的平方成正比。

只要某一步的误差在\((0, 1)\)区间内,则误差会快速的缩小,所以算法可以快速地找到平方根。

与牛顿法的关系

这个算法其实就是在用牛顿法求一个函数的根,所以是牛顿法的一个特列。

为什么呢?牛顿法求根的迭代公式是:\(x_{k} = x_{k-1} - f(x_{k-1}) / f'(x_{k-1})\)。

这里令 \(f(x) = m - x^2\),则有 \(f'(x) = -2x\),所以 \(f(x) / f'(x) = -(m - x^2)/(2x)\),牛顿法迭代公式就变为 \(x_{k} = x_{k-1} + (m - x_{k-1}^2)/(2x_{k-1})
= x_{k-1} + m / (2x_{k-1}) - x_{k-1} / 2
= x_{k-1} / 2 + m / (2x_{k-1})\),这也就是Heron's的迭代公式了。

求平方根算法 Heron’s algorithm的更多相关文章

  1. [转载]求平方根sqrt()函数的底层算法效率问题

    我们平时经常会有一些数据运算的操作,需要调用sqrt,exp,abs等函数,那么时候你有没有想过:这个些函数系统是如何实现的?就拿最常用的sqrt函数来说吧,系统怎么来实现这个经常调用的函数呢? 虽然 ...

  2. [algorithm] Dijkstra双栈算法表达式求值算法

    一.原理 Dijkstra所做的一个算法,双栈求值,用两个栈(一个保存运算符,一个用于保存操作数), 表达式由括号,运算符和操作数组成. (1).将操作数压入操作数栈 (2).将运算符压入运算符栈: ...

  3. C语言之基本算法11—牛顿迭代法求平方根

    //迭代法 /* ================================================================== 题目:牛顿迭代法求a的平方根!迭代公式:Xn+1 ...

  4. 谷歌的网页排序算法(PageRank Algorithm)

    本文将介绍谷歌的网页排序算法(PageRank Algorithm),以及它如何从250亿份网页中捞到与你的搜索条件匹配的结果.它的匹配效果如此之好,以至于“谷歌”(google)今天已经成为一个被广 ...

  5. HMM隐马尔科夫算法(Hidden Markov Algorithm)初探

    1. HMM背景 0x1:概率模型 - 用概率分布的方式抽象事物的规律 机器学习最重要的任务,是根据一些已观察到的证据(例如训练样本)来对感兴趣的未知变量(例如类别标记)进行估计和推测. 概率模型(p ...

  6. C++版 - Leetcode 69. Sqrt(x) 解题报告【C库函数sqrt(x)模拟-求平方根】

    69. Sqrt(x) Total Accepted: 93296 Total Submissions: 368340 Difficulty: Medium 提交网址: https://leetcod ...

  7. EM算法(Expectation Maximization Algorithm)

    EM算法(Expectation Maximization Algorithm) 1. 前言   这是本人写的第一篇博客(2013年4月5日发在cnblogs上,现在迁移过来),是学习李航老师的< ...

  8. [LeetCode] Sqrt(x) 求平方根

    Implement int sqrt(int x). Compute and return the square root of x. 这道题要求我们求平方根,我们能想到的方法就是算一个候选值的平方, ...

  9. 维特比算法(Viterbi Algorithm)

      寻找最可能的隐藏状态序列(Finding most probable sequence of hidden states) 对于一个特殊的隐马尔科夫模型(HMM)及一个相应的观察序列,我们常常希望 ...

随机推荐

  1. java ServletContextListener 实现UDP监听

    使用spring boot实现项目启动时的监听, UDPListener import java.io.IOException;import java.io.UnsupportedEncodingEx ...

  2. centOS7启动apache,报错Caused by: java.net.UnknownHostException: wshCentOS:

    测试方法: ping 127.0.0.1 ping localhost 解决方案: vi /etc/hosts 把127.0.0.1和wshCentOS绑定就好 按 i 进入修改模式,按 esc 退出 ...

  3. JS-数组的定义

  4. 简易数据分析 08 | Web Scraper 翻页——点击「更多按钮」翻页

    这是简易数据分析系列的第 8 篇文章. 我们在Web Scraper 翻页--控制链接批量抓取数据一文中,介绍了控制网页链接批量抓取数据的办法. 但是你在预览一些网站时,会发现随着网页的下拉,你需要点 ...

  5. ES 24 - 如何通过Elasticsearch进行聚合检索 (分组统计)

    目录 1 普通聚合分析 1.1 直接聚合统计 1.2 先检索, 再聚合 1.3 扩展: fielddata和keyword的聚合比较 2 嵌套聚合 2.1 先分组, 再聚合统计 2.2 先分组, 再统 ...

  6. WPF控件截图

    //截图         RenderTargetBitmap RenderVisaulToBitmap(Visual vsual, int width, int height)         { ...

  7. quick-cocos2dx在eclipse下的lua调试

    文中大部分内容来自http://cn.quick-x.com/?p=253,绿色标记部分为修改部分. 配置编译环境的基本步骤: 安装 Visual Studio 2012 安装 Java SDK 安装 ...

  8. Sublime Text3激活及个性化配置

    [TOC] 在我们的开发过程中,选择正确的开发工具会让我们事半功倍.作为后端开发我们熟悉的myeclipse和itellij idea这些工具我也介绍曾介绍过关于他们的安装及破解.但是我们并不能仅仅使 ...

  9. pdf.js跨域加载文件

    pdf.js一个基于Html的工具类,熟悉pdf.js的朋友们很清楚,pdf.js帮助我们做了很多事.尤其金融类网站会产生很多的报表.需要在线预览.pdf.js绝对是我们的首选 本地预览 在pdf.j ...

  10. Mysql-巧用join来优化sql

    0. 准备相关表来进行接下来的测试 相关建表语句请看:https://github.com/YangBaohust/my_sql user1表,取经组 +----+-----------+------ ...