欢迎关注WX公众号:【程序员管小亮】

python学习之路 - 从入门到精通到大师

文章目录

  • 欢迎关注WX公众号:【程序员管小亮】
    • [python学习之路 - 从入门到精通到大师](https://blog.csdn.net/TeFuirnever/article/details/90017382)
    • 一、背包问题
      • 1)简单算法
      • 2)动态规划
    • 二、背包问题FAQ
      • 1)再增加一件商品将如何呢
      • 2)行的排列顺序发生变化时结果将如何
      • 3)可以逐列而不是逐行填充网格吗
      • 4)增加一件更小的商品将如何呢
      • 5)可以偷商品的一部分吗
      • 6)旅游行程最优化
      • 7)处理相互依赖的情况
      • 8)计算最终的解时会涉及两个以上的子背包吗
      • 9)最优解可能导致背包没装满吗
    • 三、最长公共子串
      • 1)绘制网格
      • 2)填充网格
      • 3)揭晓答案
      • 4)最长公共子序列
      • 5)最长公共子序列之解决方案
    • 四、总结
    • 参考文章

一、背包问题

再来看看第8章的背包问题(《算法图解》学习笔记(八):贪婪算法和NP完全问题(附代码))。假设你是个小偷,背着一个可装4磅东西的背包。可盗窃的商品有如下3件:

为了让盗窃的商品价值最高,该选择哪些商品?

1)简单算法

最简单的算法如下:

尝试各种可能的商品组合,并找出价值最高的组合。

这样可行,但速度非常慢。在有3件商品的情况下,你需要计算8个不同的集合;有4件商品时,需要计算16个集合。每增加一件商品,需要计算的集合数都将翻倍!这种算法的运行时间为 O(2n)O(2^n)O(2n),真的是慢如蜗牛。

只要商品数量多到一定程度,这种算法就行不通。在第8章(《算法图解》学习笔记(八):贪婪算法和NP完全问题(附代码)),你学习了如何找到近似解,这接近最优解,但可能不是最优解。

那么如何找到最优解呢?

2)动态规划

答案是使用动态规划!下面来看看动态规划算法的工作原理。动态规划先解决子问题,再逐步解决大问题。

对于背包问题,你先解决小背包(子背包)问题,再逐步解决原来的问题。

动态规划是一个难以理解的概念,如果你没有立即搞懂,也不用担心,我们将研究很多示例。先来演示这种算法的执行过程。

每个动态规划算法都从一个网格开始,背包问题的网格如下:

网格的各行为商品,各列为不同容量(1~4磅)的背包。所有这些列都需要,因为它们将帮助你计算子背包的价值。

网格最初是空的。你将填充其中的每个单元格,网格填满后,就找到了问题的答案!一定要跟着做,创建网格,然后一起来填满它。

  1. 吉他行

后面将列出计算这个网格中单元格值的公式。先来一步一步做,首先来看第一行。

这是吉他行,意味着你将尝试将吉他装入背包。在每个单元格,都需要做一个简单的决定:偷不偷吉他?别忘了,你要找出一个价值最高的商品集合。

第一个单元格表示背包的容量为1磅。吉他的重量也是1磅,这意味着它能装入背包!因此这个单元格包含吉他,价值为1500美元。

下面来开始填充网格。

与这个单元格一样,每个单元格都将包含当前可装入背包的所有商品。

来看下一个单元格。这个单元格表示背包的容量为2磅,完全能够装下吉他!

这行的其他单元格也一样。别忘了,这是第一行,只有吉他可供你选择。换言之,你假装现在还没法盗窃其他两件商品。

此时你很可能心存疑惑:原来的问题说的是4磅的背包,为何要考虑容量为1磅、2磅等的背包呢?前面说过,动态规划从小问题着手,逐步解决大问题。这里解决的子问题将帮助解决大问题。请接着往下读,稍后你就会明白的。

此时网格应类似于下面这样。

别忘了,你要做的是让背包中商品的价值最大。这行表示的是当前的最大价值。它指出,如果你有一个容量4磅的背包,可在其中装入的商品的最大价值为1500美元。

你知道这不是最终的解。随着算法往下执行,你将逐步修改最大价值。

  1. 音响行

我们来填充下一行——音响行。你现在出于第二行,可偷的商品有吉他和音响。在每一行,可偷的商品都为当前行的商品以及之前各行的商品。因此,当前你还不能偷笔记本电脑,而只能偷音响和吉他。

先来看第一个单元格,它表示容量为1磅的背包。在此之前,可装入1磅背包的商品的最大价值为1500美元。

该不该偷音响呢?

背包的容量为1磅,能装下音响吗?音响太重了,装不下!由于容量1磅的背包装不下音响,因此最大价值依然是1500美元。

接下来的两个单元格的情况与此相同。在这些单元格中,背包的容量分别为2磅和3磅,而以前的最大价值为1500美元。

由于这些背包装不下音响,因此最大价值保持不变。

背包容量为4磅呢?终于能够装下音响了!原来的最大价值为1500美元,但如果在背包中装入音响而不是吉他,价值将为3000美元!因此还是偷音响吧。

你更新了最大价值!如果背包的容量为4磅,就能装入价值至少3000美元的商品。在这个网格中,你逐步地更新最大价值。

  1. 笔记本电脑行

下面以同样的方式处理笔记本电脑。笔记本电脑重3磅,没法将其装入容量为1磅或2磅的背包,因此前两个单元格的最大价值还是1500美元。

对于容量为3磅的背包,原来的最大价值为1500美元,但现在你可选择盗窃价值2000美元的笔记本电脑而不是吉他,这样新的最大价值将为2000美元!

对于容量为4磅的背包,情况很有趣。这是非常重要的部分。当前的最大价值为3000美元,你可不偷音响,而偷笔记本电脑,但它只值2000美元。

价值没有原来高。但等一等,笔记本电脑的重量只有3磅,背包还有1磅的容量没用!

在1磅的容量中,可装入的商品的最大价值是多少呢?你之前计算过。

根据之前计算的最大价值可知,在1磅的容量中可装入吉他,价值1500美元。因此,你需要做如下比较。

你可能始终心存疑惑:为何要计算小背包可装入商品的最大价值呢?但愿你现在明白了其中的原因!余下了空间时,你可根据这些子问题的答案来确定余下的空间可装入哪些商品。笔记本电脑和吉他的总价值为3500美元,因此偷它们是更好的选择。

最终的网格类似于下面这样。

答案如下:将吉他和笔记本电脑装入背包时价值最高,为3500美元。

你可能认为,计算最后一个单元格的价值时,我使用了不同的公式。那是因为填充之前的单元格时,我故意避开了一些复杂的因素。其实,计算每个单元格的价值时,使用的公式都相同。这个公式如下:

你可以使用这个公式来计算每个单元格的价值,最终的网格将与前一个网格相同。现在你明白了为何要求解子问题吧?你可以合并两个子问题的解来得到更大问题的解。

二、背包问题FAQ

你可能还是觉得这像是变魔术,来回答一些常见的问题。

1)再增加一件商品将如何呢

假设你发现还有第四件商品可偷——一个iPhone!

此时需要重新执行前面所做的计算吗?不需要。别忘了,动态规划逐步计算最大价值。到目前为止,计算出的最大价值如下。

这意味着背包容量为4磅时,你最多可偷价值3500美元的商品。但这是以前的情况,下面再添加表示iPhone的行。

最大价值可能发生变化!请尝试填充这个新增的行,再接着往下读。

我们从第一个单元格开始。iPhone可装入容量为1磅的背包。之前的最大价值为1500美元,但iPhone价值2000美元,因此该偷iPhone而不是吉他。

在下一个单元格中,你可装入iPhone和吉他。

对于第三个单元格,也没有比装入iPhone和吉他更好的选择了。

对于最后一个单元格,情况比较有趣。当前的最大价值为3500美元,但你可偷iPhone,这将余下3磅的容量。

3磅容量的最大价值为2000美元!再加上iPhone价值2000美元,总价值为4000美元。新的最大价值诞生了!

最终的网格如下。

问题:沿着一列往下走时,最大价值有可能降低吗?

答案:不可能。每次迭代时,你都存储当前的最大价值。最大价值不可能比以前低!

2)行的排列顺序发生变化时结果将如何

答案会随之变化吗?

问题:假设你按如下顺序填充各行:音响、笔记本电脑、吉他。网格将会是什么样的?

网格将类似于下面这样。

答案:没有变化。也就是说,各行的排列顺序无关紧要。

3)可以逐列而不是逐行填充网格吗

就这个问题而言,这没有任何影响,但对于其他问题,可能有影响。

4)增加一件更小的商品将如何呢

假设你还可以偷一条项链,它重0.5磅,价值1000美元。前面的网格都假设所有商品的重量为整数,但现在你决定把项链给偷了,因此余下的容量为3.5磅。在3.5磅的容量中,可装入的商品的最大价值是多少呢?不知道!因为你只计算了容量为1磅、2磅、3磅和4磅的背包可装下的商品的最大价值。现在,你需要知道容量为3.5磅的背包可装下的商品的最大价值。

由于项链的加入,你需要考虑的粒度更细,因此必须调整网格。

5)可以偷商品的一部分吗

假设你在杂货店行窃,可偷成袋的扁豆和大米,但如果整袋装不下,可打开包装,再将背包倒满。在这种情况下,不再是要么偷要么不偷,而是可偷商品的一部分。如何使用动态规划来处理这种情形呢?

答案是没法处理。使用动态规划时,要么考虑拿走整件商品,要么考虑不拿,而没法判断该不该拿走商品的一部分。但使用贪婪算法可轻松地处理这种情况!首先,尽可能多地拿价值最高的商品;如果拿光了,再尽可能多地拿价值次高的商品,以此类推。

例如,假设有如下商品可供选择。

藜麦比其他商品都值钱,因此要尽量往背包中装藜麦!如果能够在背包中装满藜麦,结果就是最佳的。

如果藜麦装完后背包还没满,就接着装入下一种最值钱的商品,以此类推。

6)旅游行程最优化

假设你要去伦敦度假,假期两天,但你想去游览的地方很多。你没法前往每个地方游览,因此你列个单子。

对于想去游览的每个名胜,都列出所需的时间以及你有多想去看看。根据这个清单,你能确定该去游览哪些名胜吗?

这也是一个背包问题!但约束条件不是背包的容量,而是有限的时间;不是决定该装入哪些商品,而是决定该去游览哪些名胜。请根据这个清单绘制动态规划网格,再接着往下读。

网格类似于下面这样。

你画对了吗?请填充这个网格,决定该游览哪些名胜。答案如下。

7)处理相互依赖的情况

假设你还想去巴黎,因此在前述清单中又添加了几项。

去这些地方游览需要很长时间,因为你先得从伦敦前往巴黎,这需要半天时间。如果这3个地方都去玩,是不是要4.5天呢?

不是的,因为不是去每个地方都得先从伦敦到巴黎。到达巴黎后,每个地方都只需1天时间。因此玩这3个地方需要的总时间为3.5天(半天从伦敦到巴黎,每个地方1天),而不是4.5天。

将埃菲尔铁塔加入“背包”后,卢浮宫将更“便宜”:只要1天时间,而不是1.5天。如何使用动态规划对这种情况建模呢?

没办法建模。动态规划功能强大,它能够解决子问题并使用这些答案来解决大问题。但仅当每个子问题都是离散的,即不依赖于其他子问题时,动态规划才管用。这意味着使用动态规划算法解决不了去巴黎玩的问题。

8)计算最终的解时会涉及两个以上的子背包吗

为获得前述背包问题的最优解,可能需要偷两件以上的商品。但根据动态规划算法的设计,最多只需合并两个子背包,即根本不会涉及两个以上的子背包。不过这些子背包可能又包含子背包。

9)最优解可能导致背包没装满吗

完全可能。假设你还可以偷一颗钻石。

这颗钻石非常大,重达3.5磅,价值100万美元,比其他商品都值钱得多。你绝对应该把它给偷了!但当你这样做时,余下的容量只有0.5磅,别的什么都装不下。

三、最长公共子串

通过前面的动态规划问题,你得到了哪些启示呢?

  • 动态规划可帮助你在给定约束条件下找到最优解。在背包问题中,你必须在背包容量给定的情况下,偷到价值最高的商品。
  • 在问题可分解为彼此独立且离散的子问题时,就可使用动态规划来解决。

要设计出动态规划解决方案可能很难,这正是本节要介绍的。下面是一些通用的小贴士:

  • 每种动态规划解决方案都涉及网格。
  • 单元格中的值通常就是你要优化的值。在前面的背包问题中,单元格的值为商品的价值。
  • 每个单元格都是一个子问题,因此你应考虑如何将问题分成子问题,这有助于你找出网格的坐标轴。

下面再来看一个例子。假设你管理着网站dictionary.com。用户在该网站输入单词时,你需要给出其定义。但如果用户拼错了,你必须猜测他原本要输入的是什么单词。例如,Alex想查单词fish,但不小心输入了hish。在你的字典中,根本就没有这样的单词,但有几个类似的单词。

在这个例子中,只有两个类似的单词,真是太小儿科了。实际上,类似的单词很可能有数千个。

Alex输入了hish,那他原本要输入的是fish还是vista呢?

1)绘制网格

用于解决这个问题的网格是什么样的呢?要确定这一点,你得回答如下问题。

  • 单元格中的值是什么?
  • 如何将这个问题划分为子问题?
  • 网格的坐标轴是什么?

在动态规划中,你要将某个指标最大化。在这个例子中,你要找出两个单词的最长公共子串。hish和fish都包含的最长子串是什么呢?hish和vista呢?这就是你要计算的值。

别忘了,单元格中的值通常就是你要优化的值。在这个例子中,这很可能是一个数字:两个字符串都包含的最长子串的长度。

如何将这个问题划分为子问题呢?你可能需要比较子串:不是比较hish和fish,而是先比较his和fis。每个单元格都将包含这两个子串的最长公共子串的长度。这也给你提供了线索,让你觉得坐标轴很可能是这两个单词。因此,网格可能类似于下面这样。

如果这在你看来犹如巫术,也不用担心。这些内容很难懂,但这也正是我到现在才介绍它们的原因!

2)填充网格

现在,你很清楚网格应是什么样的。填充该网格的每个单元格时,该使用什么样的公式呢?由于你已经知道答案——hish和fish的最长公共子串为ish,因此可以作点弊。

即便如此,你还是不能确定该使用什么样的公式。计算机科学家有时会开玩笑说,那就使用 费曼算法(Feynman algorithm)。这个算法是以著名物理学家理查德·费曼命名的,其步骤如下:

  1. 将问题写下来。
  2. 好好思考。
  3. 将答案写下来。

    计算机科学家真是一群不按常理出牌的人啊!

实际上,根本没有找出计算公式的简单办法,你必须通过尝试才能找出管用的公式。有些算法并非精确的解决步骤,而只是帮助你理清思路的框架。请尝试为这个问题找到计算单元格值的公式。给你一点提示吧:下面是这个单元格的一部分。

其他单元格的值呢?别忘了,每个单元格都是一个子问题的值。为何单元格(3, 3)的值为2呢?又为何单元格(3, 4)的值为0呢?

3)揭晓答案

最终的网格如下。

我使用下面的公式来计算每个单元格的值。

实现这个公式的伪代码类似于下面这样。

if word_a[i] == word_b[j]:cell[i][j] = cell[i-1][j-1] + 1
else:cell[i][j] = 0

查找单词hish和vista的最长公共子串时,网格如下。

需要注意的一点是,这个问题的最终答案并不在最后一个单元格中!对于前面的背包问题,最终答案总是在最后的单元格中。但对于最长公共子串问题,答案为网格中最大的数字——它可能并不位于最后的单元格中。

我们回到最初的问题:哪个单词与hish更像?hish和fish的最长公共子串包含三个字母,而hish和vista的最长公共子串包含两个字母。因此Alex很可能原本要输入的是fish。

4)最长公共子序列

假设Alex不小心输入了fosh,他原本想输入的是fish还是fort呢?

我们使用最长公共子串公式来比较它们。

最长公共子串的长度相同,都包含两个字母!但fosh与fish更像。

这里比较的是最长公共子串,但其实应比较最长公共子序列:两个单词中都有的序列包含的字母数。如何计算最长公共子序列呢?

下面是用于计算fish和fosh的最长公共子序列的网格的一部分。

你能找出填充这个网格时使用的公式吗?最长公共子序列与最长公共子串很像,计算公式也很像。

5)最长公共子序列之解决方案

最终的网格如下。

下面是填写各个单元格时使用的公式。

伪代码如下。

if word_a[i] == word_b[j]:
cell[i][j] = cell[i-1][j-1] + 1
else:
cell[i][j] = max(cell[i-1][j], cell[i][j-1])

本章到这里就结束了!它绝对是本书最难理解的一章

动态规划都有哪些实际应用呢?

  • 生物学家根据最长公共序列来确定DNA链的相似性,进而判断度两种动物或疾病有多相似。最长公共序列还被用来寻找多发性硬化症治疗方案。
  • 你使用过诸如git diff等命令吗?它们指出两个文件的差异,也是使用动态规划实现的。
  • 前面讨论了字符串的相似程度。编辑距离(levenshtein distance)指出了两个字符串的相似程度,也是使用动态规划计算得到的。编辑距离算法的用途很多,从拼写检查到判断用户上传的资料是否是盗版,都在其中。
  • 你使用过诸如Microsoft Word等具有断字功能的应用程序吗?它们如何确定在什么地方断字以确保行长一致呢?使用动态规划!

四、总结

  • 需要在给定约束条件下优化某种指标时,动态规划很有用。
  • 问题可分解为离散子问题时,可使用动态规划来解决。
  • 每种动态规划解决方案都涉及网格。
  • 单元格中的值通常就是你要优化的值。
  • 每个单元格都是一个子问题,因此你需要考虑如何将问题分解为子问题。
  • 没有放之四海皆准的计算动态规划解决方案的公式。

参考文章

  • 《算法图解》

《算法图解》学习笔记(九):动态规划(附代码)相关推荐

  1. 算法图解学习笔记02:递归和栈

    计算机内存原理 要说递归和栈的问题,首先就要说下计算机内存的基本原理.简单理解计算机内存原理可以将一台电脑看作超市的存包柜,每个柜子都有柜号(即计算机中的地址,如0x000000f).当需要将数据存储 ...

  2. 算法图解学习笔记01:二分查找大O表示法

    二分查找 二分查找又称折半查找,其输入的必须是有序的元素列表.二分查找的基本思想是将n个元素分成大致相等的两部分,取a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止:如果x<a[ ...

  3. 算法图解学习笔记01之二分查找

    不知道可以学到第几章,就不立flag了,容易倒 你要学会的不是写算法而是何时何地用何算法 package 第一章; //数组有序,要求快速查找出数据 public class BinarySearch ...

  4. 算法图解——学习笔记

    文章目录 算法简介 **第二章 选择排序** 数组与链表 排序算法 算法简介 算法:一组完成任务的指令,任何片段都可以视为算法. 第一章 算法集合: 算法种类 定义 二分法 一种查询方法,通过将查找特 ...

  5. 算法图解学习笔记02之选择排序

    package 第二章;import java.util.Arrays;//找出列表中最小(大)的,放入新列表(O(n)),再次找 public class SelectionSort {public ...

  6. 一文弄懂元学习 (Meta Learing)(附代码实战)《繁凡的深度学习笔记》第 15 章 元学习详解 (上)万字中文综述

    <繁凡的深度学习笔记>第 15 章 元学习详解 (上)万字中文综述(DL笔记整理系列) 3043331995@qq.com https://fanfansann.blog.csdn.net ...

  7. 步步为营 .NET 代码重构学习笔记 九

    步步为营 .NET 代码重构学习笔记系列 步步为营 .NET 代码重构学习笔记 一.为何要代码重构 步步为营 .NET 代码重构学习笔记 二.提炼方法(Extract Method) 步步为营 .NE ...

  8. 吴恩达《机器学习》学习笔记九——神经网络相关(1)

    吴恩达<机器学习>学习笔记九--神经网络相关(1) 一. 非线性假设的问题 二. 神经网络相关知识 1.神经网络的大致历史 2.神经网络的表示 3.前向传播:向量化表示 三. 例子与直觉理 ...

  9. 算法训练营学习笔记1

    算法训练营学习笔记 贪心算法 心算法总是做出当前最好的选择,期望通过局部最优选择得到全局最优的解决方案.从问题的初始解开始,一步歩地做出当前最好的选择,逐步逼近问题的目标,尽可能得到最优解: 贪心本质 ...

  10. IOS学习笔记(九)之UIAlertView(警告视图)和UIActionSheet(操作表视图)基本概念和使用方法...

    IOS学习笔记(九)之UIAlertView(警告视图)和UIActionSheet(操作表视图)基本概念和使用方法 Author:hmjiangqq Email:jiangqqlmj@163.com ...

最新文章

  1. 怎么修改网页服务器数据库连接,如何修改网页服务器数据库连接
  2. 打破“维度的诅咒”,机器学习降维大法好
  3. docker 异常:“fork/exec /proc/self/exe: no such file”
  4. java环境变量设置--编写一年java,竟不会配变量了
  5. php按每小时显示数据,mysql-PHP按某月统计每个小时生成的数据
  6. stotybord如何添加子视图_SQL复杂查询-子查询
  7. python大作业爬虫_爬虫大作业
  8. python标准库使用教程_Python标准库概览
  9. Java基础学习总结(92)——Java编码规范之排版、注释及命名
  10. python快速入门神器 知乎_Python爬虫偷懒神器!快速一键生成Python爬虫请求头
  11. 人生苦短我用python(02)动态加载模块
  12. Luogu2279[HNOI2003] 消防局的设立
  13. 关于JavaScript DOM 编程艺术这本书
  14. python 报童模型
  15. php中验证码如何实现登录验证,php登录验证码怎么实现
  16. ​目标检测算法——YOLOv5/YOLOv7改进之结合Criss-Cross Attention
  17. Design contains shelved or modified (but not repoured) polygons. The result of DRC is not correct.
  18. Java中outer标签的用法
  19. python中简述对象和类的关系_Python笔记-习题42 对象、类及从属关系
  20. Android 模拟器硬件加速

热门文章

  1. 二级C语言打不开考生文件夹,注意:下面出现的“考生文件夹”均为c:\wexam\25160001。 (1) 在考生文件夹下有一个工程文件sjt3.vbp - 赏学吧...
  2. Material Design-Surface平面第二篇
  3. JNI 手动释放内存(避免内存泄露)
  4. nginx网关与gateway网关的区别
  5. 2017阿里巴巴校招在线笔试——货架格子编号
  6. 基于GitHub的敏捷学习方法之道与术
  7. jquery局部打印插件使用
  8. matlab2015 colormap,matlab中colormap函数
  9. 重庆链家租房数据分析
  10. C++标准库string类型那些事儿