最简单易懂的10堂算法入门课——算法思想之一:贪心算法

听起来高大上的“算法”,其实一点也不难!

专栏《最简单易懂的10堂算法入门课》用最简洁的语言和逻辑,脱离编程语言的束缚,在最短时间内,从算法概念/程序结构/数据结构/算法思想/应用方法这五个方面,跟您一起,轻松地理解算法知识,掌握算法思维。

小复习

  • 程序结构有哪些?

顺序/循环/分支

  • 数据结构有哪些?

低阶的有:数组/链表/堆栈/队列

高阶的有:树/集/映射/图

最简单易懂的10堂算法入门课——算法思想之一:贪心算法

算法思想

算法思想就是解题思路,是算法程序的提纲,常见的算法思想就4种:

贪心算法(Greedy Algorithm)

分治算法(Divide and Conquer)

动态规划(Dynamic Programming)

穷举搜索(Exhaustive Searching)

可以说90%以上的算法,都来自这4种基本思想,掌握它们,再设计算法时,就“才思泉涌,下笔有神”了。

最简单易懂的10堂算法入门课——算法思想之一:贪心算法

贪心算法

贪心算法,听着不怎么熟悉,其实是最最常用常见的算法了,因为它的思想太朴素了——

一个问题有点难,一时找不到最优解,那么就把原问题拆成几个小问题分别求每个小问题的最优解,再把这些“局部最优解”叠起来,就“算是”整体最优解了。

通过我上面的描述,也可以猜出一个结论——

贪心算法得到最终结果,很有可能并不是全局最优解,但是可能会比较接近最优解。

在每一步中,选择目前最优的策略而并不考虑长远,这种短视被叫做——

贪心

最简单易懂的10堂算法入门课——算法思想之一:贪心算法

贪心——只看到局部最优解

再合理不过了,对吧?

三步完成贪心算法

  • 第一步

定义什么是最优解

  • 第二步

定义什么是子问题的最优解

  • 第三步

分别求出子问题的最优解再堆叠出全局最优解

就是这么简单!

最简单易懂的10堂算法入门课——算法思想之一:贪心算法

上个例子加强理解——0-1背包问题

0-1背包问题是一种非常常见且典型的问题,问题的模式的是这样:

有一个背包,最多能承载150斤的重量,现在有7个物品,重量分别为[35, 30, 60, 50, 40, 10, 25],它们的价值分别为[10, 40, 30, 50, 35, 40, 30],现在想要用这个背包背走最多价值的物品,怎么背?

最简单易懂的10堂算法入门课——算法思想之一:贪心算法

0-1背包问题

其实这类问题再常见不过了,对于选择的物品有一个总和限制,比如这里的重量,又希望另一个属性的总和最大,比如这里的价值。

每一个物品,要么选择(1),要么放弃(0),这就是0-1背包问题。

实操一下吧

还是分三步:

  • 第一步

定义什么是最优解

这里很明显,在重量限制范围内,价值最大的选择,就是最优解。

  • 第二步

定义什么是子问题的最优解

子问题定义为每一次拿什么物品,那么子问题的最优解怎么定义?

其实有很多定义方法,这里选择一种最直接的方式——

认为每次都尽量选择当前价值最高的物品,这就是“局部最优解”

最简单易懂的10堂算法入门课——算法思想之一:贪心算法

  • 第三步

分别求出子问题的最优解再堆叠出全局最优解

按照制订的规则来选一下吧,顺序是:4 2 6 5 。

最终的总重量是:130。

最终的总价值是:165

子问题最优解还有不同的定义方法

子问题的最优解有各种定义方法,这些方法决定了最终的结果究竟有多接近真正的全局最优解。

比如上面的0-1背包问题,还可以这样定义:

认为每次都选择当前物品中重量最小的,策略是: 6 7 2 1 5。

可以看出这样比上面能够多放入一样物品,总重量是140,但是总价值是155却并没有上面方法更优。

最简单易懂的10堂算法入门课——算法思想之一:贪心算法

还可以这样定义:

认为每次要选择“价值密度”最高的物品,(价值密度是指单位重量的价值),顺序是:6 2 7 4 1, 总重量是150,总价值是170

可以看出最后一种定义方法最优,那么是不是以后0-1背包问题都这样去定义子问题呢?

其实未必

一种定义方式往往可能只在一种特定条件下是最优,新条件下可能最不是最优了

最简单易懂的10堂算法入门课——算法思想之一:贪心算法

用贪心算法三个核心问题:

1 为什么不直接求全局最优解?

这是使用贪心算法的前提,即出现这几种情况:

原问题复杂度过高

求全局最优解的数学模型难以建立

求全局最优解的计算量过大

没有太大必要一定要求出全局最优解,“比较优”就可以。

那么几乎99%就要使用贪心算法的思想来解决问题。

2 怎样把原问题分解成子问题?

一般根据问题的逻辑解构有这么几种分法:

  • 按串行任务分

时间串行的任务,按子任务来分解,即每一步都是在前一步的基础上再选择当前的最优解。

  • 按规模递减分

规模较大的复杂问题,可以借助递归思想(见第2课),分解成一个规模小一点点的问题,循环解决,当最后一步的求解完成后就得到了所谓的“全局最优解”。

  • 按并行任务分

这种问题的任务不分先后,可能是并行的,可以分别求解后,再按一定的规则(比如某种配比公式)将其组合后得到最终解。

最简单易懂的10堂算法入门课——算法思想之一:贪心算法

3 如何知道贪心法的结果逼近了真正的最优解

依靠逻辑分析,依靠以往经验,“感觉”比较使用这种方法误差不会超出忍受范围了,就可以。

算法无“最优”

学完这一讲,我们应该对算法设计有这么一个新印象了——

它跟纯数学不同,并不追求绝对的最优解,因为追求的过程需要考虑:

  • 成本

耗费多少资源,花掉多少编程时间。

  • 速度

计算量是否过大,计算速度能否满足要求。

  • 价值

得到了最优解与次优解是否真的有那么大的差别,还是说差别可以忽略。

理解了贪心算法,几乎就等同理解了大部分的算法设计者是怎么思考问题的。


分享到:


相關文章: