帮你精通JavaScript:二分查找算法求任何方程的根


帮你精通JavaScript:二分查找算法求任何方程的根

市面上,二分查找算法的实现方法有很多。其共同的特点是,看起来似乎很容易,但是面试的时候,即使是同样的题目却很难复现,更不要说遇到变通的题目。

本文将会帮助你完全掌握binary-search.

1.回到数学的问题的起点

设有连续函数 f(x), 求 f(x) = 0 在区间(a, b) 上的根。我们假设在区间内的解是存在的,则必然有下面的关系:

f(a) < 0 < f(b)

函数 f 在区间a,b内必然至少有一个点为0,由此分情况讨论:

  1. f(x) > 0,则 f(x) = 0 的情况在 a 与 x 之间
  2. f(x) < 0,则 f(x) = 0 的情况在 x 与 b 之间。

如此循环而不断缩小查找区间,最终达到最小区间,跳出运行。

算法复杂度为:Θ(log(L/T)), 其中L(Length)为区间长度, T(tolerance) 为容差。

2.关于抽象思考

在撰写具体的实现之前,荡开一笔,探讨你的大脑中如何思考 f(x) > 0 这个表达式。

是读作 “f(x) 大于 0” 或者 “f(x) greater than 0" 吗?

比较符号 ">” 是最原始的运算符号,当我们用它思考的时候,大脑就不由自主的陷入到繁琐的细节之中,因此导致我们常常遇到的情况。明明一个算法很简单,比如“冒泡排序”,”插入排序“,一看就懂,但是真要动手写一个时候,往往要耗费半个多小时。

其症结只在于,我们的思考纠缠到了具体的实现上,计算机能够轻松的运行 < > 等运算符号,但是我们用以思考的语言没有将其向更高维度抽象出来。

再进一步思考,“f(x) 大于 0” 对大脑而言究竟是什么意思? 对,很简单,大于0 是正值。当我们用 “f(x)是正值" 取代”f(x)大于0"之时,大脑与直觉瞬间被激活。因为思考“正值在右边,负值在左边”,比思考“大于0的数就在数轴的右边,而小于0的数字就在数轴数轴的左边”更加符合直觉,更加不需要思考。

由此,定义出positive和negetive两个函数:

function positive(x) { return x > 0; }
function negative(x) { return x < 0; }

虽然很简单,却能帮助大脑从案牍劳形的低级思考中解放出来。

3.算法实现

闲话少叙,直接上算法实现:

function search(f, neg_point, pos_point) {
    let midpoint = average(neg_point, pos_point);
    if (close_enough(neg_point, pos_point)) {
        return midpoint;
    } else {
        let test_value = f(midpoint);
        if (positive(test_value)) {
            return search(f, neg_point, midpoint);
        } else if (negative(test_value)) {
            return search(f, midpoint, pos_point);
        } else {
            return midpoint;
        }
    }
}

第一行中,将average单独拎出来,遵循了与定义positive和negetive相同的逻辑,即在思考过程中不纠缠于初级的表达语言。

function average(a, b) {
  return (x + y) / 2;
}

函数close_enough定义tolerance容差。

function close_enough(x, y) {
    return abs(x - y) < 0.001;
}

定义绝对值:

function abs(x) {
    return x >= 0 ? x : -x;
}

4.测试与验证

先测试求平方根sqrt,比如求数字 7 的平方根:

console.log(search(x => x * x -7, 0, 7));
//: 2.64593505859375
//或者求立方根:
console.log(search(x => x * x * x - 57, 0, 57));
: 3.8482131958007812
//或者求三角函数的值:
console.log(search(x => Math.sin(x) - 1, 0, 1)

或者负责的多元求根公式:

x^3 - 3x^2 - 101 = 0

console.log(search(x => x**3 -3*x**2 - 101, 0, 101)
//求得:
5.900630950927734

最后三角函数的求值:

console.log(search(x => Math.sin(x) - 1, 0, Math.PI/2))

求得:

 1.570412831597925

验证:

> Math.asin(1)
1.5707963267948966

更进一步的抽象

以上定义的函数search以及验证:

function search(f, neg_point, pos_point)

的过程中,有一步必需的人工操作,就是验证 f(a) 与 f(b) 这两个值是正值还是负值,然后才能带入到方程中的正确位置。

由此,我们将其进一步抽象:

function binary_search(f, a, b) {
    let a_value = f(a);
    let b_value = f(b);
    return negative(a_value) && positive(b_value) ? search(f, a, b)
           : negative(b_value) && positive(a_value) ? search(f, b, a)
           : error("values are not of opposite sign");
}

进一步抽象的方案就能求任何方程的解。

展开阅读全文

页面更新:2024-03-13

标签:方程   算法   立方根   求根   连续函数   数轴   平方根   负值   抽象   直觉   函数   符号   大脑   题目   定义

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top