不久前,美团的面试官问我,"我便考你一考,你知道开平方么?便写一个罢"
我心里只隐约记得大学课程的《数值分析》里介绍了一种叫做牛顿迭代的算法进行计算,算法细节却早已忘光。于是我便写了一个愧对职业素养的实现:暴力破解
1.暴力破解
思路很简单,我随便找一个初始值,求出平方与目标比对一下,大了就减小,小了就增大。反复尝试总会有对的时候
/**
* 暴力破解,傻瓜方案
*/
@Test
public void foolSqrt() {
double target = 9;
double point = target / 2;
int times = 0;
double precision = 1e-2;
double gap = 1;
while (Math.abs(gap) > precision) {
if ((target - point * point) > 0) {
point += precision;
} else {
point -= precision;
}
gap = target - point * point;
times++;
}
}
为了求出9的平方根,尝试 : 151次 , point : 3.000000000000032 , point的平方 : 9.000000000000192 , point平方与target的差值 : -1.9184653865522705E-13
我有几张阿里云幸运券分享给你,用券购买或者升级阿里云相应产品会有特惠惊喜哦!把想要买的产品的幸运券都领走吧!快下手,马上就要抢光了。
2.牛顿迭代
牛顿迭代其实不仅仅是用来做开平方的,而是快速逼近求得方程的逼近解。以下内容来自果壳文章《求牛顿开方法的算法及其原理,此算法能开任意次方吗?》
假设方程在
附近有一个根,那么根据f(x)在x0附近的值和斜率,就能估计f(x)和x轴的交点用。迭代式子:
依次计算
、
、
、……,那么序列将无限逼近方程的根。
![]()
用牛顿迭代法开平方】令:
![]()
所以f(x)的一次导是:
![]()
牛顿迭代式:
随便一个迭代的初始值,例如
,代入上面的式子迭代。例如计算
,即a=2。
![]()
![]()
![]()
……
计算器上可给出![]()
根据果壳文章以上阐述,由此我们可以写出如下代码:
/**
* 牛顿迭代开平方
*/
@Test
public void newtonSqrt() {
int target = 9;
int point = 1;
double precision = 1e-2;
int times = 0;
while (Math.abs(target - point * point) > precision) {
point = (point + target / point) / 2;
times ++;
}
}
求9的平方根,尝试 : 3次 , point : 3 , point的平方 : 9 , point平方与target的差值 : 0
到此为此,似乎问题已经得到了圆满的答案。但只有一个正确答案如何对得起编程孔乙己的title。紧接着我就发现了更有趣的解法