算法浅谈——二分法的进阶版,三分算法

本文始发于个人公众号:TechFlow

之前的文章当中我们详细阐述了二分法,尤其是讨论了我们在编写代码时候的边界问题。传送门:

算法浅谈——人人皆知却很多人写不对的二分法

今天这一篇文章,我们继续来讲算法,我们不讲二分法了。来讲讲二分法的进阶版——三分法

是的,你们没有看错,这不是我任性起的名字,而是实实在在的有这个算法。不过如果去搜索引擎里搜,大概率会搜到摄影的三分构图法,而很难搜索三分查找的算法。

主要原因是它和二分法比较起来要小众得多,几乎所有的算法书籍当中都没有三分法的介绍。它更多的是存在于ACM-ICPC这类算法竞赛当中,不过小众没关系,只要有用就好。三分法的原理也很简单,和二分法几乎一模一样,只不过我们分隔区间的时候,不是将区间一分为二,而是一分为三。之后,我们同样通过缩小区间的方法来确定要查找的值所在。

看到这里,我相信你们应该都能理解算法原理,但是肯定会有一个问题要问:既然分成两份就能解决问题,我们为什么要分成三份呢?

在回答这个问题之前,我们先来看另一个问题。在数学上,二分法究竟解决了一个什么问题?

还记得二分法使用的前提么?数组必须是有序的,所以二分法其实解决的是单调函数的求解的问题。只要数组是有序的,根据函数的定义就可以看做是一个将数组下标映射到数组取值的函数。显然,这是一个单调函数。我们通过二分法查找其中的一个元素v,本质其实是查找 f(x) = v
这个函数的解。

所以,二分法使用的场景是单调函数,也就是一次函数。那如果我要搜索二次函数的最小值,用二分法可行吗?

图片说明

显然不可行,因为我们在取完mid之后, 并不知道答案可能出现在左右哪个区间。
这个时候就需要三分法出场了。

图片说明

三分***将区间分成三份,这个我们都已经知道了。分成三份,自然需要两个端点。这两个端点各有一个值,我们分别叫做m1和m2。我们要求的是函数的最小值,所以我们要想极值逼近。

但是我们有两个中间点,该怎么逼近呢?

我们直接根据函数图像来分析,根据上图我们可以看出来,m1和m2的函数值和它们距离极值点的远近是有关系的。离极值点越近,函数值越小(也有可能越大,视函数而定)。在上图当中,

,所以m2离极值点更近。我们要缩小区间范围,逼近极值点,所以我们应该让l=m1

图片说明

这里有一点小问题,我们怎么确定极值点在m2和m1中间呢?万一在m2的右侧该怎么办呢?

我们画出图像来看,这种情况其实并没有区别,我们只会抛弃区间[l, m1]
,并不会影响极值点。

会不会极值点在m1左侧呢?这是不可能的,因为如果极值点在m1左侧,那么m2距离极值点一定比m1远,这种情况下m2处的函数值是不可能小于m1的。

也就是说,三分法的精髓在于,每次通过比较两个值的大小,缩小三分之一的区间。直到最后区间的范围小于我们设定的阈值为止。算法并不难理解,但是当我们真正碰到二次函数的极值问题的时候,如果没有事先接触过三分法,很难一下想到算法。

三分法本身并不难,我们理解了算法之后,写出伪代码来就很容易了:

def trichotomy():
    l, r = start, end
    epsilon = 1e-6
    # 我们自定义的终止条件是区间长度小于1d-6
    while l + epsilon < r:
        margin = (r - l) / 3.0
        m1 = l + margin
        m2 = m1 + margin
        if f(m1) <= f(m2):
            r = m2
        else:
            l = m1
    return l

最后, 我们看一道三分法相关的算法题,来实际演练一下三分法吧。

这是一道POJ3737,链接如下:http://poj.org/problem?id=3737

我来简单介绍一下题意。题意很简单,当下我们拥有一块布,我们知道布的大小,要将布做成一个圆锥形。要使得圆锥形的体积最大,假设布在加工的过程当中没有损耗,请问这个圆锥的体积、底面半径和高分别是多少?

我这里先不放答案,大家不妨自己想一下,实在做不出来,在公众号内回复三分法答案,获取题解。

如果觉得文章有所帮助,请扫码给个关注吧:

图片说明

全部评论

相关推荐

09-28 17:38
门头沟学院 Java
小肥罗:众生皆吗喽,那满山吗喽也是我腚最红!!!
我的秋招日记
点赞 评论 收藏
分享
点赞 评论 收藏
分享
搜索部&nbsp;首先说下timeline8.18,投递8.19,约一面8.21,晚上一面call约二面8.22,上午二面下午oc周末等待(8.23,8.24)8.25,offer一年前,我还是懵懵懂懂,高考完的暑假,只会提前学学高数,未来的画像是什么?我或许无法预测。开学后,自学Python,接单,无数个客户的ddl,偷偷摸摸一个人找自习的地方,这一步步竟然为后来的我,搭建工程能力的基础。大一上,我也要感谢我的第一位老板,让我接触到了实习,师兄带着我一步步入门,看他们写的飞书文档。大一下,导师带我参与企业项目,这让我渐渐发现,应该去实践,增长见识,而非局限当下,盯着自己的小新pro。不久后,第一波投递开始,结果当然是约面极少。盯着简历上的文字和ssob,我开始思考,确实很多可以去提升。带着些许不甘心,继续沉淀,慢慢的约面也越来越多,有的时候两天7场,准备完就接着下一个日程。这一次,也许是刚好到位吧,比较match,面试答的流利,关关难关关过,成为度孝子展望未来,依然是重重挑战,果然只有收到offer的那一刻是开心的。愿在百度星海拆解的每一段代码,都能成为丈量宇宙的诗行;此志终赴星河,而今迈步重铸天阶。屏幕前的你们,在无数个向星海奔赴的日夜,一定一定,会在未来化作群星回响的征程——请永远相信此刻埋首耕耘的自己!!!
一天三顿半:???百度提前批发 offer了?不是统一和正式批排序完再发吗我靠
百度求职进展汇总
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务