Math.sqrt()
JavaScript中存在一个Math.sqrt()函数,可以传入一个数字,返回这个数字的开平方根的结果。
自己写一个mySqrt()
我们可以自己模拟实现一个和Math.sqrt()类似功能的函数,这里我们只探讨两种方法(思想)来解决这个问题,一是二分法,二是牛顿迭代法,而至于更底层性能相对更好的方法,这里不做讨论。
二分法
给定一个数字n,我们可以取它的中间数mid,假设mid就是这个数字n开方的结果,用中间数mid进行平方,如果结果大于这个数字n,则说明数字n开方的结果应该比mid要小,即它位于mid的左侧区间,再取这个左侧区间的中间数,直到上一次取的mid和当前次区间取得的mid的间隔达到精度时,则返回结果;反之亦然。
例如:给定一个数字16,取它的中间数也就是8,8的平方是64,比16要大,所以下次我们取区间[0, 8]的中间数也就是4,4的平方是16,正好相等,所以返回结果。
function mySqrt(n) {
if (n < 0) {
return NaN;
}
if (n === 0) {
return 0;
}
let low = 0;
let high = n;
let prevMid;
let mid = (low + high) / 2;
do {
if (mid * mid > n) {
high = mid;
} else {
low = mid;
}
// 记录上一次取的中间数
prevMid = mid;
// 记录当前的中间数
mid = (low + high) / 2;
} while (Math.abs(prevMid - mid) > 1e-15);// 对两次的中间数进行对比 如果间隔小于等于精准区间时则说明达到了所需要的精准度 则退出循环
return parseFloat(mid.toFixed(15));
}
牛顿迭代法
具体步骤如下:
求出根号a的近似值:首先随便猜一个近似值x,然后不断令x等于x和a/x的平均数,迭代个六七次后x的值就已经相当精确了。
例如,我想求根号2等于多少。假如我猜测的结果为4,虽然错的离谱,但你可以看到使用牛顿迭代法后这个值很快就趋近于根号2了:
( 4 + 2/4 ) / 2 = 2.25
( 2.25 + 2/2.25 ) / 2 = 1.56944..
( 1.56944..+ 2/1.56944..) / 2 = 1.42189..
( 1.42189..+ 2/1.42189..) / 2 = 1.41423..
….
具体思想可以自行搜索。简而言之,如下图
x^2 = a的解,也就是函数f(x) = x^2 – a与x轴的交点。可以在x轴上先任选一点x0,则点(x0, f(x0))在f(x)上的切线,与x轴的交点为x1,它们满足切线的方程:f(x0)=(x0-x1)f’(x0),可得x1更接近最终的结果,解方程得到:
x1 = (x0 + (a/x0))/2
以x1为新的x0,按照切线的方法依次迭代下去,最终求得符合精确度要求的结果值。它的实现代码如下:
// 用牛顿迭代法实现Math.sqrt()
function mySqrtByNewton(n) {
if (n < 0) {
return NaN;
}
if (n === 0) {
return 0;
}
var val = n;//最终
var preVal;//保存上一个计算的值
do {
preVal = val;
val =(val + n / val) / 2;
} while (Math.abs(val-preVal) > 1e-15);
return +val.toFixed(15);
}