求平方根算法 Heron’s algorithm
求平方根问题
概述:本文介绍一个古老但是高效的求平方根的算法及其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\)。算法描述如下:
- 初始化 \(x = 1\);
- 计算 \(\Delta_k = |x_k - x_{k-1}|\);
- 若\(\Delta_k < \epsilon\),返回\(x\),算法结束;
- \(x \leftarrow \frac{x}{2} + \frac{m}{2 x}\);
- 返回第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]\)区间:
- 初始化 \(u=0\);
- 若 \(0.5 \leq m \leq 2\),返回 \(u\),算法结束;
- 若 \(m > 2\): \(m \leftarrow m / 4\), \(u \leftarrow u+1\);
- 若 \(m < 1\): \(m \leftarrow m * 4\), \(u \leftarrow u-1\);
- 返回第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的更多相关文章
- [转载]求平方根sqrt()函数的底层算法效率问题
我们平时经常会有一些数据运算的操作,需要调用sqrt,exp,abs等函数,那么时候你有没有想过:这个些函数系统是如何实现的?就拿最常用的sqrt函数来说吧,系统怎么来实现这个经常调用的函数呢? 虽然 ...
- [algorithm] Dijkstra双栈算法表达式求值算法
一.原理 Dijkstra所做的一个算法,双栈求值,用两个栈(一个保存运算符,一个用于保存操作数), 表达式由括号,运算符和操作数组成. (1).将操作数压入操作数栈 (2).将运算符压入运算符栈: ...
- C语言之基本算法11—牛顿迭代法求平方根
//迭代法 /* ================================================================== 题目:牛顿迭代法求a的平方根!迭代公式:Xn+1 ...
- 谷歌的网页排序算法(PageRank Algorithm)
本文将介绍谷歌的网页排序算法(PageRank Algorithm),以及它如何从250亿份网页中捞到与你的搜索条件匹配的结果.它的匹配效果如此之好,以至于“谷歌”(google)今天已经成为一个被广 ...
- HMM隐马尔科夫算法(Hidden Markov Algorithm)初探
1. HMM背景 0x1:概率模型 - 用概率分布的方式抽象事物的规律 机器学习最重要的任务,是根据一些已观察到的证据(例如训练样本)来对感兴趣的未知变量(例如类别标记)进行估计和推测. 概率模型(p ...
- C++版 - Leetcode 69. Sqrt(x) 解题报告【C库函数sqrt(x)模拟-求平方根】
69. Sqrt(x) Total Accepted: 93296 Total Submissions: 368340 Difficulty: Medium 提交网址: https://leetcod ...
- EM算法(Expectation Maximization Algorithm)
EM算法(Expectation Maximization Algorithm) 1. 前言 这是本人写的第一篇博客(2013年4月5日发在cnblogs上,现在迁移过来),是学习李航老师的< ...
- [LeetCode] Sqrt(x) 求平方根
Implement int sqrt(int x). Compute and return the square root of x. 这道题要求我们求平方根,我们能想到的方法就是算一个候选值的平方, ...
- 维特比算法(Viterbi Algorithm)
寻找最可能的隐藏状态序列(Finding most probable sequence of hidden states) 对于一个特殊的隐马尔科夫模型(HMM)及一个相应的观察序列,我们常常希望 ...
随机推荐
- 用Python和Pandas以及爬虫技术统计历史天气
背景 最近在计划明年从北京rebase到深圳去,所以最近在看深圳的各个方面.去年在深圳呆过一段时间,印象最深的是,深圳总是突然就下雨,还下好大的雨.对于我这种从小在南方长大但是后面又在北京呆了2年多的 ...
- vs2005 打不开resoure view?
原来是破解版 vc助手惹的祸. 解决方法:卸载vc助手或者换一个注册的vc助手
- php_review_day1
php中的小知识点(小白笔记整理):-----------------------------------------------------读取本地文件内的数据: file_get_contents ...
- Pivotal:15分钟部署你的应用
“ 本篇文章介绍的是PaaS平台Pivotal Cloud Foundry(以下简称PCF)的初步使用,相比于传统的IaaS平台(比如阿里云),PCF可实现快速迭代开发与部署,让您专注于业务开发.” ...
- vue更改了数据但是视图却没有更新
今日总结:让钩子函数背锅的一上午,跪求原谅 需求:页面刷新时,保留当前页面的选中状态 解决思路:在data里设置一下初始值,保证首次进入有默认选中状态,其次再操作时,把选中的状态存sessionSto ...
- HelloDjango 系列教程:创建 Django 博客的数据库模型
文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 设计博客的数据库表结构 博客最主要的功能就是展示我们写的文章,它需要从某个地方获取博客文章数据才能把文章展示出来,通常来说这个 ...
- angular6组件封装以及发布到npm
一.创建angular项目 ng new myFirstDemo //angular-cli新建项目ng g m testm //新建模块ng g c testm/headertest //新建组件 ...
- 归并排序(从上到下、从下到上)——C语言
归并排序 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归 ...
- codeforces 371A K-Periodic Array
很简单,就是找第i位.i+k位.i+2*k位...........i+m*k位有多少个数字,计算出每个数出现的次数,找到出现最多的数,那么K-Periodic的第K位数肯定是这个了.这样的话需要替换的 ...
- 学习vue感触
大学还没毕业,想着先找工作,感觉计算机专业在老家做没有太大的发展,于是就在大学所在城市找了份工作.来到公司的第一天,带我的师傅让我学习vue.之前完全没有接触过框架,而且专业知识比较薄弱,前几天一直处 ...