知其所以然(续)

查了一下,上篇知其所以然(以学习算法为例) 是08年7月写的,现在已经是10年11月,过去了两年零4个月,这说明了三件事情:1,一个问题其实你可以一直放在脑子里面,利用暗时间 对其软泡硬磨,时间足够久你总会有一点新的感悟,问题其实就像那句老话说的那样,不怕贼偷就怕贼惦记,聚精会神的思考一天,也许比不上惦记一个星期(据说数学家庞加莱就特别会惦记问题 )。 2,事实上,当你感觉懂了的时候,你至少得反问自己一句,真的懂了吗?当你确信自己真的懂了的时候,你至少得讲给别人听,别人听懂了吗?考察你自己是否真 懂了的一个很好的依据是,你是否有一种“哦,原来是这样啊,这下再也不可能忘记了”的感觉。3,我其实没有忘记这个博客。如我之前说的,记录只是学习和思考的副作用 ,只要还在学习和思考,就必然会有新的记录。

我有一个习惯,看定理必看证明。一个你不明白其证明的定理在我看来比不知道这个定理还要糟糕,因它给你造成一种懂了的错觉。在没有明白背后的证明之前,任何一个定理对你来说都是等价的——等价于背乘法口诀 (只不过有的长一点有的短一点)。一个原本美妙的定理,把其证明扔掉就是真正的买椟还珠,暴殄天物。

从现实意义来说,去理解一个定理的证明会带来巨大的好处,首当其冲的好处就是你很难再忘掉它 。这一点其实很容易解释——在理解一个定理的证明之前,定理对你而言是一堆没有内在联系的词句,而在理解了证明之后,定理就归约为 证 明它所需的条件加上逻辑,“逻辑”本来就存在于你的大脑里面,而证明的过程中除了公理和用到的常见定理(往往没几条)之外,宽泛地说,需要你去记的,一般 来说也只有一个或两个关键的insights,也就是我们常说的证明中的神来之笔,比如几何证明里面的某条看上去莫名其妙的辅助线,一旦你知道了这条辅助 线,那么整个证明就毫无难处,那么该定理的信息量便直接缩减为一条辅助线的信息量;虽然看上去这一步信息并没有缩减多少,但是如果你考虑到类似的辅助线不 仅会用在这个特定的定理上,往往会在很多地方用到。很多关键的证明手法是通用的。那么其实你就是把所有以这个辅助线为关键证明手法的定理的集合的信息量归 约为了这条辅助线。如果你进而甚至能够理解了作这条辅助线的思想精髓,那就更牛逼了,因为解决问题的思路更具有一般性,理解了寻找正确的辅助线的思路,你 就根本不需要去记得某条特定辅助线的作法,你就把所有以作一条或几条辅助线为证明核心的定理的集合的信息量归约为了这个“寻找辅助线的思路”。

这是一个树状的知识结构,越往上层走,需要记忆的节点就越少 。所谓触类旁通者,其实便是因为他擅长去理解解法背 后的更具一般性的东西。所以我还有一个习惯,就是看到美妙的证明和解法总是会去一遍又一遍的去反复揣摩,试图理解想出这个证明的人到底是怎么想出来的,有 没有什么一般性的方法可循,很多时候,在这样揣摩的过程中,你会理解到更深刻的东西,对问题性质更深刻的认识,对解决问题的思路更深刻的认识,这些认识不 仅对于你理解当前这个定理或问题有极大的帮助,同时也有助于你解决以后会遇到的表面不同但本质一样的问题。

与看定理必看证明类似,看一个问题的解法,必然要看解法所诞生的过程,背后是否隐藏着更具一般性的解决问题的思路和原则。否则一个解法就只是一个问题的解法,跟背口诀一样。即便记住了也无法推广,即便当时记住了也容易遗忘。

举个经典的例子:每本算法书都会讲动态规划,每本讲动态规划的书都会讲背包问题,每次讲背包问题都会讲可重复背包和01背包,我们就拿《Algorithms》这本还算不错 的算法书对背包问题的讲解来说吧,重复背包问题的递归公式是这样的:

K(W) = max { K(W-Wi) + Vi : Wi <= W }

这个公式的理解倒是很简单:为了把问题降阶,我们在最终的最优解里面去掉一个元素,对这个元素的可能性进行讨论,它必然是任何Vi之一(前提是Wi <= W,否则就装不下),而在去掉这个元素之后,剩下的元素肯定构成问题 K(W-Wi) 的最优解,于是递归关系出现了。

此外也可以这样来理解:要拿一组最优元素,那么总得开始一个个拿吧,对第一个拿的元素进行讨论,而问题的最优解等于讨论的各个分支的最优解中的最优者;如果拿掉Vi之后,剩下来要怎么拿才能最优呢?这就是一个 K(W-Wi) 的问题了。

01背包问题就大不一样了——每个物品都只有一件,拿掉之后就不能再拿了。我们不妨看看重复背包问题的解法是不是能用到01背包上呢?还是讨论第一 个拿的元素,设被拿掉的是第i个元素,问题就归结为把剩下的物品(注意,可拿的物品少了一件)最优地装入容量为 W-Wi 的包里,所以,问题的参数便变成了两个,一个是背包剩余容量 W-Wi,另一个是剩余可拿的物品集合 S\{i} (表示去掉i之后的子集),显而易见第二个参数是物品集合的各种可能的子集,那么其可能性个数就是 2^n ,这就导致子问题的个数是 2^n, 由于要依次计算每个子问题,那么算法复杂度显然也是 2^n ,是不可接受的。

那么,《Algorithms》上又是怎么来讲解01背包问题的解法的呢?以下是原文:

Our earlier subproblems now become completely useless. We must therefore refine our concept of a subproblem to carry additional information about the items being used. We add a second parameter, 0 <= j <= n: K(W, j) = maximum value achievable using a knapsack of capacity w and items 1..j: The answer we seek is K(W, n).

首先作者说了,之前重复背包问题的解法在这里完全废掉了,所以我们必须重新定义子问题,并且子问题的条件必须要包含目前拿剩下的物品。以上这些都还不错,关键是接下来就让人吐血了。作者接着说道,我们 给子问题加上一个新的参数j…

凭什么啊?

还是让我们回顾一下这样一幅经典的漫画 吧:

“我们给子问题加上一个参数j”,这就像你在看数学证明时看到无比邪恶的“我们考虑 …“一样,一看到这样的句 子,你就知道,这个问题的证明远远不像看上去那么简单,之所以你一路看下去理解上全无困难,那完全是因为作者直接把最重要的一个insight告诉你了, 举个很简单的例子,证明素数无最大,谁都会第一时间想到去反证:假设存在一个最大的素数P,那么找到比P大的素数就是证明中最关键的一步,怎么找的?一般 书上是不会说的,你会看到书上这样说:假设P是最大的素数,那么我们考虑P’ = 小于等于P的所有素数的乘积+1。那么P’一来显然大于P,二来不能被小于它的所有素数整除,那么P’就成了大于P的素数。

如果你经常注意反证法,你会发现一个有趣的现象,反证法里面经常会有这样一句“我们考虑”,而“我们考虑”后面几乎肯定接着一个天外飞仙一般的 insight。素数无最大这个古老的证明里面的“我们考虑”尚算是比较有迹可循的(我们想要构造一个更大的素数,而素数的等价定义就是“不能被小于它的 所有素数整除,为了达到这个目的,构造的方法就较明显了)。但是有非常非常多的证明,其中关键的一步就跟嗑药磕出来做梦做出来走路跌跟头跌出来的一样(不 信去翻一翻《Proofs from THE Book 》),让你完全不知道他怎么想到的。

话说回来,虽然有很多数学证明的关键步骤是很难逆向工程的(因为很多时候想出那个关键步骤的本人其实也是尝试了各种方法,撞了无数堵墙,在寻求证法 的尝试空间中作了N次回溯才“妙手偶得”,与其说是妙手偶得,不如说是绞尽脑汁),但并非全无章法可循,否则陶哲轩也不会写出《Solving Mathematical Problems 》这样的著作来,而求解问题也就成了真正的Black Art了。

算法的解法则比精妙的数学证明稍加更容易逆向工程一点。只要你有耐心仔细地去琢磨算法的关键步骤和本质,总能从中窥探到一些更general的思想和思路来。

此外,很多经典问题,算法书上的讲法虽然时时令我们失望,但如果去网上一搜,则通常会发现更优秀的解释来。比如背包问题就是如此 。

简单地说,如果你对于每个问题都能真正弄清以下这几个问题的答案,那么可以肯定的是,你的理解,记忆,以及学习的效率都会得到质的提高:

  • 为什么这种解法是对的?
  • 为什么那种解法是错的?
  • 为什么这种解法不是最优的?
  • 证明为什么没有更优的解法。

回到人民群众喜闻乐见的经典例子:背包问题。为什么01背包问题的正确(高效)算法是正确(高效)的。表面的解释是,因为01背包问题的子问题定义 是 K(W, j),其两个维度相乘的可能性一共有nW种,也就是说一共要计算nW个子问题,而计算每个子问题的复杂度是O(1)的。

但是如果仅仅满足于这样的解释,可以说是隔靴搔痒,并没有触及到本质。算法本质上可以看做是在一个解空间当中的搜索问题,所以要分析一个算法的好坏,首先弄清它的解空间的结构,然后分析它是怎么来探索这个解空间的。

弄清解空间的是第一步,例如排序算法,其解空间可以看做是所有可能的下标排列组合,其中有且仅有一个排列是正确的排序排列(简单起见假设元素各不相 同)。那么一个算法在探索这个解空间方面的行为就决定了它的效率高低,最简单的,如果一个算法每次只能检查解空间中的一个点,那么这个算法的复杂度就是解 空间的大小。对排序算法而言也就是n!。从这个角度来看,我们就会很容易的发现,所有基于比较的排序算法,其复杂度为什么是以O(nlogn)为下界的, 因为一次比较操作最多有两个结果,a>b或a<b,既然只有两种结果,那么最多只能将解空间进行2分,如果每次都能完美的2分,那么找到那个 唯一点最终需要的步骤就是log(n!) = O(nlogn)。如此就不难理解什么基于比较的排序算法的复杂度最好不过如此了 。

回到01背包问题,01背包问题的解空间其实也是类似的。一次选取就是一个01数组,其中每个元素代表其所对应的物品要不要选取。很显然,这个解空 间的大小是2^n。在01背包的算法里面,每当我们解出K(W, j)(需要O(W)次计算)之后,解空间就会被折半(排除掉1/2的可能性),一共如此做n次,就能得到最终解。由于每次折半的代价是O(W),便不难理 解为什么算法复杂度是O(nW)了。

那么,为什么每次计算出K(W,j)就能使解空间折半呢?那就需要来看看这个算法是如何探索解空间的,算法探索解空间的方式在其递归公式里面:

K(W, j) = max { K(W, j-1), K(W-Wj, j – 1)  + Vj }

也就是说,首先看你要不要选取第一个物品,有两种可能性(两个分支),每个分支都是一个更低阶的子问题,即在其中的任意一个分支下都要决定要不要选 取第二个物品(又是两个分支),如此下递归去,可以构建出一棵有2^n方个叶子节点的树,每条从根结点到叶子节点的路径“01..101”就对应一个解, 其中每个分叉代表“选”或“不选”当前的物品。

建立在对这个解空间的理解上,我们再来看为什么01背包问题的正确解法能做到O(nW)。(首先你最好将这棵树画在纸上,其中每个节点都是一个子问 题K(W,j),每条分叉都是0或1。)当我们计算出所有的K(W, 1)(需要O(W)次操作)之后,我们容易注意到,所有离叶子节点的距离为1的内部节点K(W, 2)到叶子节点的两个分支都必然只能取其一了,也就是说,有一半的叶子节点被排除掉了(对解空间折半)。当我们进而计算出K(W,2)之后,同样的道理, 我们容易看到,到叶子节点距离为2的内部节点的两个分支也只能取其一了,这就进而再次将解空间折半。由于每次折半需要O(W)的复杂度,所以就不难理解算 法的总复杂度为O(nW)了。另一种理解的方法是,当我们计算出K(W,j)的时候,从内部节点K(W,j)到根节点的唯一路径便确定了。经过O(nW) 次计算,从根节点到那个唯一解(叶子节点)的路径便完全确定了。

知道怎么做是从正确(高效)解法得到的,而知道为什么必须得那样做则往往是从错误(低效)的解法当中得到的。

然而遗憾的是,绝大多数算法书或教程都只顾一上来就告诉你正确的做法是什么,对于一些常见的错误解法,或者常见的低效解法,却根本不加分析。经验告 诉我们,理解错误的做法为什么错误同样甚至更为重要,往往是在理解了错误的解法为什么错误之后,我们才能深刻的体会到为什么正确的解法是如此正确。

还是拿经典的背包问题来作例子,你几乎看不到哪本书会告诉你一个典型的低效解法为什么低效的深刻原因。我们都知道动态规划的核心在于子问题的划分, 同样的问题,不同的划分办法得到的复杂度完全不一样。前面已经提到了,重复背包问题的思路在01背包问题上会带来指数级的复杂度,但是为什么呢?如果你满 足于说:因为如果拿重复背包问题的思路来解01背包问题,那么子问题定义的第二个维度(物品的子集)(见前文)是指数级的,那么要计算所有子问题,当然是 指数级的。那么你只是看到这个问题的表象。

如果从对解空间的探索方式来说,可以容易看出这个现象的本质,我们回顾一下01背包问题的正确(高效)算法:

K(W, j) = max { K(W, j-1), K(W-Wj, j – 1)  + Vj }

这个算法讨论的是两种情况,“要”或者“不要”选取第j个物品,这两种情况所对应的解空间是完全不交的,这就有效地将解空间划分为了不重复的两个部分。

而再来看利用重复背包问题思路的解法:

K(W, S) = max { K(W-Wi, S\{i}) + Vi : Wi <= W }

这里讨论的是首先拿掉哪一个物品,还是那句话,讨论的每一个分支都对应了算法对解空间的一个切分,我们容易看出,在“先拿物品i”和”先拿物品j “这两个分支里面,存在大量的重复,因为先拿物品i再拿j,和先拿物品j再拿i对应的是完全一样的一组选取。事实上,如果你将这个递归公式画成树状结构, 会发现有n!个叶子节点。n!是什么概念?01背包问题的解空间大小本质上就只有2^n次方,穷举也不过O(2^n)的复杂度,结果这样一切分却变成了 n!,可见这种对解空间的切分方法的冗余度是多么高了。你不妨看看,每一次计算K(W, S)子问题能对解空间排查多少呢?是否能像前面正确的算法那样,每次都能有效排查一半情况?理解了这一点之后,我们便注意到在划分解空间,也就是定义子问 题的时候的一个原则,就是在建立递归公式的时候,尽量将解空间进行不交的切分。同时我们便有了趁手的工具去分析一个动态规划的解法的效率。

最后再举一个例子:算法书上几乎必讲的霍夫曼树。你所看的算法书在讲霍夫曼树的时候给了证明吗?讲过霍夫曼树的历史八卦 吗?也许你看了霍夫曼树的构造方法之后觉得:“哦,这样啊,显然”。但是你可曾想到,在最优编码这个问题上,连香农本人之前给出的解法 都只是suboptimal的,而且霍夫曼本人在得到这个算法之前也是绞尽脑汁几近放弃。如果你10分钟就“理解”了,那么百分之百只是背了课文而已。

扩展阅读:

1.Bop的豆瓣主页:http://book.douban.com/subject/3004255/

2.互动网购买链接:http://www.china-pub.com/38070

3.“《编程之美》,IT人求职面试必读”链接:
http://www.google.com.hk/search?complete=1&hl=zh-CN&newwindow=1&q =编程之美+-+微软技术面试心得+邹欣&meta=&aq=f&oq=

4.《我是一只IT小小鸟》豆瓣链接:http://book.douban.com/subject/4006425/

转载于:https://www.cnblogs.com/bvbook/archive/2010/11/26/1888348.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/435341.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

geoserver安装(war安装+exe安装)

下载 官网&#xff1a;http://geoserver.org/ 方法一 当前只有war版本的 下载结果 tomcat安装最好不要使用exe版本的&#xff0c;因为会出现问题&#xff0c;页面加载不出来&#xff0c;所以大家只需要下载下来解压即可 然后将geoserver安装包中的war文件放到webappx下 然后…

LSGO软件技术团队2015~2016学年第十六周(1214~1220)总结

团队简述&#xff1a; LSGO软件技术团队成立于2010年10月&#xff0c;主要从事的应用方向为互联网与移动互联网&#xff08;UI设计&#xff0c;前端开发&#xff0c;后台开发&#xff09;&#xff0c;地理信息系统&#xff1b;研究方向为大数据处理与机器学习。成立几年来为学校…

LSGO软件技术团队2015~2016学年第十七周(1221~1227)总结

团队简述&#xff1a; LSGO软件技术团队成立于2010年10月&#xff0c;主要从事的应用方向为互联网与移动互联网&#xff08;UI设计&#xff0c;前端开发&#xff0c;后台开发&#xff09;&#xff0c;地理信息系统&#xff1b;研究方向为大数据处理与机器学习。成立几年来为学校…

postgresql+postgis安装

下载 官网https://www.postgresql.org/ 作者的电脑为win 下载结果为 postgis要选择下载对应版本的 官网http://www.postgis.org/ 以下安装最好装到除c盘之外的&#xff0c;否则有些安装需要权限&#xff0c;一般为d盘 用户名和密码都设置为postgres postgis安装教程 之前设置过…

aop实现原理_SpringAOP原理分析

目录Spring核心知识SpringAOP原理AOP编程技术什么是AOP编程AOP底层实现原理AOP编程使用Spring核心知识Spring是一个开源框架&#xff0c;Spring是于2003年兴起的一个轻量级的Java开发框架&#xff0c;由Rod Johnson在其著作Expert One-On-One J2EE Development and Design中阐述…

MAVEN安装和配置

maven官网下载 https://maven.apache.org/download.cgi

MIPS架构的医院智能导诊系统设计

摘要&#xff1a;通过研究基于MIPS架构的SMP8654芯片的硬件架构&#xff0c;并且利用芯片内部的图形加速引擎GFX的方式实现了具有高清视频显示和图片文字处理功能的播放器。系统以嵌入式Linux和MiniGUI为平台设计了智能导诊系统&#xff0c;提高了医院的导诊就医的服务效率。智…

arcgis在线地图插件安装

软件下载链接 https://download.csdn.net/download/qq_39397927/15761863

hadoop namenode启动不了_集群版hadoop安装,写给大忙人看的

导语 如果之前的单机版hadoop环境安装满足不了你&#xff0c;集群版hadoop一定合你胃口&#xff0c;轻松入手。目录 集群规划前置条件配置免密登录 3.1 生成密匙 3.2 免密登录 3.3 验证免密登录集群搭建 4.1 下载并解压 4.2 配置环境变量 4.4 修改配置 4.4 分发程序 4.5 初始化…

patch文件制作

一、为单个文件打补丁1、首先我用的ubuntu12 os&#xff0c;cat >>test0<<eof但是这命令执行得是root身份more命令功能&#xff1a;让画面在显示满一页时暂停&#xff0c;此时可按空格健继续显示下一个画面&#xff0c;或按Q键停止显示。more test0:查看test0内容2…

java string逆序_java经典入门算法题,java初学者必备

java经典入门算法题开头求关注警告喜欢这样文章的可以关注我&#xff0c;我会持续更新&#xff0c;你们的关注是我更新的动力&#xff01;需要更多java学习资料的也可以私信我&#xff01;祝关注我的人都&#xff1a;身体健康&#xff0c;财源广进&#xff0c;福如东海,寿比南山…

u-boot的patch文件制作

首先明白为什么要制作patch文件&#xff0c;因为u-boot的移植过程需要根据实际需要修改通用u-boot&#xff0c;如果每次手工修改的话&#xff0c;太麻烦&#xff0c;所以用了patch文件一步到位&#xff0c;这点类似于makefile的作用&#xff0c;哈哈1.了解 diff 和 patch。diff…

postgis创建空间数据库(pgadmin4)

打开软件 看我之前的安装步骤的用户密码为postgres 点击save 查看是否具有空间数据 1.查看是否存在空间数据表 2.查看是否具有空间函数

SQL Server 2012 安装

SQL Server 2012 安装 安装包在这里&#xff1a;https://pan.baidu.com/s/1_sgxN8P-pzj7uZeAR0VlKQ 提取码&#xff1a;mnvj 文件比较大&#xff0c;慢慢下载吧 下载好后是这样的&#xff1a; 双击打开&#xff0c;点击 setup.exe 安装&#xff1a; 选择“安装”&#xff0…

矢量数据导入数据库

打开数据导入工具 设置数据库链接 用户名和密码都是postgres 链接成功 arcgis10.3之前编码是gbk之后为utf-8 目前作者用的是10.2版本导出的数据 shp文件名和路径名必须为英文 添加数据 成功导入 追加表的属性要一致 查看导入数据 查看是否导入成功 创建两个字段

@qualifier注解_常见的 Spring 注解概览

点击上方 Java后端&#xff0c;选择 设为星标优质文章&#xff0c;及时送达从Java5.0开始&#xff0c;Java开始支持注解。Spring做为Java生态中的领军框架&#xff0c;从2.5版本后也开始支持注解。相比起之前使用xml来配置Spring框架&#xff0c;使用注解提供了更多的控制Sprin…

经纬度转XY坐标-批量转换

excel存储的经纬度坐标&#xff0c;如何批量转换为XY平面坐标呢&#xff1f; 1.把度分秒转成度小数&#xff1a; 函数为&#xff1a;MID(B2,1,3)MID(B2,5,2)/60MID(B2,8,5)/3600 转换失效的需要补成两位数&#xff0c;如图 2.将文件另存为97-2003版本的excel&#xff0c;后缀…

数据库备份与恢复

备份 数据库的恢复 新建数据库邮件恢复

Arcgis将shp图投影坐标转换地理坐标,投影失败的问题

问题来源&#xff1a; 目的&#xff1a;shp图需要将投影坐标系去掉&#xff0c;即投影坐标系转换为地理坐标系 正常操作&#xff1a; 法1&#xff1a;使用Arcgis中工具箱-数据管理工具-投影与变换-要素-投影&#xff0c;&#xff1a;这个工具进行坐标转换 法2&#xff1a;转换…