武汉博文官方博客http://blog.csdn.net/bvbook

技术凝聚实力 专业创新出版 与向上的心合作 共同成长!

原创 《编程之美》读书笔记(三):烙饼问题与搜索树收藏

       《编程之美》读书笔记(三):烙饼问题与搜索树
                                 作者:薛笛
前面已经写了一些关于烙饼问题的简单分析,但因为那天太累有些意犹未尽,今天再充实一些内容那这个问题研究透。我想,通过这篇文章,我们就可以把这一类问题搞懂。再遇到优化问题,如果我们想不到别的办法,就可以采用搜索树算法来解决,至少我们不至于拿不出解决方案。前面我们已经知道,关于一摞烙饼的排序问题我们可以采用递归的方式来完成。其间我们要做的是尽量调整UpperBound和LowerBound,已减少运算次数。对于这种方法,在算法课中我们应该称之为:Tree Searching Strategy。即整个解空间为一棵搜索树,我们按照一定的策略遍历解空间,并寻找最优解。一旦找到比当前最优解更好的解,就用它替换当前最优解,并用它来进行“剪枝”操作来加速求解过程。
书中给出的解法就是采用深度优先的方式来遍历这棵搜索树,例如要排序[4,2,1,3],最大反转次数不应该超过(4-1)*2=6次,所以搜索树的深度也不应大于6,搜索树如下图所示:
这里只列到第三层,其中被画斜线的方块由于和上层的某一节点的状态重复而无需再扩展下去(即便扩展也不可能比有相同状态的上层节点的代价少)。我们可以看到在右子树中的一个分支,只需要用3次反转即可完成,我们的目标是如何更为快速有效的找到这一分支。直观上我们可以看到:基本的搜索方法要先从左子树开始,所以要找到本例最佳的方案的代价是很高的(利用书中的算法需要查找292次)。
既然要遍历搜索树,就有广度优先和深度优先之分,可以分别用栈和队列来实现(当然也可以用递归的方法)。那么如何能更有效地解决问题呢?我们主要考虑一下几种方法:
(1)       爬山法
该方法是在深度优先的搜索过程中使用贪心方法确定搜索方向,它实际上是一种深度优先搜索策略。爬山法采用启发式侧读来排序节点的扩展顺序,其关键点就在于测度函数f(n)的定义。我们来看一下如何为上例定制代价函数f(n),以快速找到右子树中最好的那个分支(很像贪心算法,呵呵)。
我们看到在[1,2,4,3]中,[1,2,3]已经相对有序,而[4]位与他们之间,要想另整体有序,需要4次反转;而[3,1,2,4]中,由于[4]已经就位,剩下的数变成了长度为3的子队列,而子队列中[1,2]有序,令其全体有序只需要2次反转。
所以我们的代价函数应该如下定义:
1 从当前状态的最后一个饼开始搜索,如果该饼在其应该在的位置(中间断开不算),则跳过;
2 自后向前的搜索过程中,如果碰到两个数不相邻的情况,就+1
这样我们就可以在本例中迅速找到最优分枝。因为在树的第一层
f(2,4,1,3)=3,f(1,2,4,3)=2,f(3,1,2,4)=1,所以我们选择[3,1,2,4]那一枝,而在[3,1,2,4]的下一层:
f(1,3,2,4)=2,f(2,1,3,4)=1,f(4,2,1,3)=2,所以我们又找到了最佳的路径。
上面方法看似不错,但是数字比较多的时候呢?我们来看书中给出的10个数的例子:
[3,2,1,6,5,4,9,8,7,0]程序给出的最佳翻转序列为{ 4,8,6,8,4,9}(从0开始算起)
那么,对于搜索树的第一层,按照上面的算法我计算的结果如下:
f(2,3,1,6,5,4,9,8,7,0)=4
       f(1,2,3,6,5,4,9,8,7,0)=3
       f(6,1,2,3,5,4,9,8,7,0)=4
       f(5,6,1,2,3,4,9,8,7,0)=3
       f(4,5,6,1,2,3,9,8,7,0)=3
       f(9,4,5,6,1,2,3,8,7,0)=4
       f(8,9,4,5,6,1,2,3,7,0)=4
f(7,8,9,4,5,6,1,2,3,0)=3
f(0,7,8,9,4,5,6,1,2,3)=3
我们看到有4个分支的结果和最佳结果相同,也就是说,我们目前的代价函数还不够“一击致命”,但是这已经比书中的结果要好一些,起码我们能更快地找到最佳方案,这使得我们在此后的剪枝过程更加高效。
爬山法的伪代码如下:
1 构造由根组成的单元素栈S
2 IF Top(s)是目标节点 THEN 停止;
3 Pop(s);
4 S的子节点按照启发式测度,由小到大的顺序压入S
5 IF 栈空 Then 失败
Else 返回2
 
              如果有时间我会把爬山法解决的烙饼问题贴在后面。
(2)       搜索策略Best-First
最佳优先搜索策略结合了深度优先和广度优先二者的优点,它采取的策略是根据评价函数,在目前产生的所有节点中选择具有最小代价值的节点进行扩展。该策略具有全局优化的观念,而爬山法则只具有局部优化的能力。具体用小根堆来实现搜索树就可以了,这里不再赘述。
(3)       算法A*
如果我们把下棋比喻成解决问题,则爬山法和Best-First算法就是两个只能“看”未来一步棋的玩家。而A*算法则至少能够“看”到未来的两步棋。
我们知道,搜索树的每一个节点的代价f*(n)=g(n)+h*(n)。其中,g(n)为从根节点到节点n的代价,这个值我们是可求的;h*(n)则是从n节点到目标节点的代价,这个值我们是无法实际算出的,只能进行估计。我们可以用下一层节点代价的最小者来替代h*(n),这也就是“看”了两步棋。可以证明,如果A*算法找到了一个解,那它一定是优化解。A*算法的描述如下:
1. 使用BestFirst搜索树
2. 按照上面所述对下层点n进行计算获得f*(n)的估计值f(n),并取其最小者进行扩展。
3. 若找到目标节点,则算法停止,返回优化解
总结:归根到底,烙饼问题之所以难于在多项式时间内解决的关键就在于我们无法为搜索树中的每一条边设定一个合理的权值。在这里,每条边的权值都是1,因为从上一个状态节点到下一个状态节点之需要一次翻转。所以我们不能简单地把每个节点的代价定义为翻转次数,而应该根据其距离最终解的接近程度来给出一个数值,而这恰恰就是该问题的难点。但是无论上面哪一种方法,都需要我们确定搜索树各个边的代价是多少,然后才能进行要么广度优先、要么深度优先、要么A*算法的估计代价。所以,在给出一个合理的代价之前,我们所有的努力都只能是帮忙“加速”,而无法真正在多项式时间内解决问题。


 

发表于 @ 2008年04月17日 15:44:00|评论(loading...)|收藏

新一篇: 《编程之美》读书笔记(四):买书折扣问题的贪心解法 | 旧一篇: 耳目一新的PHP5

用户操作
[即时聊天] [发私信] [加为好友]
武汉博文视点
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
武汉博文视点的公告
参加读者调查,免费获取新书
博文视点,年底重磅原创
Web2.0好书推荐
文章分类
收藏
    博文视点合作伙伴
    01.《程序员》杂志
    02.CSDN读书频道
    03.51CTO读书频道
    04.博客园
    05.JavaEye技术网站
    06.中国互动出版网
    07.卓越亚马逊
    08.华储网
    09.新风雨网络书城
    10.当当网
    11.IT专家网Linux频道
    博文视点图书网站
    《RESTful Web Services中文版》官方网站
    《Silverlight 2完美征程》官方网站
    《代码大全》官方网站
    《移山之道》读者社区(RSS)
    《编程之美》“智慧碰撞”专栏
    《走出软件作坊》官方论坛
    《软件调试》官方网站
    博文视点团队博客
    策划营销团队博客
    编校团队博客
    设计团队博客
    博文视点专家博客
    DBA notes-冯大辉
    Delphi专家-李维
    GIS专家-蒋波涛
    InfoQ中文站主编-霍泰稳
    ITECN博客主编-盆盆
    LVS创始人--章文嵩
    MySQL专家-简朝阳
    OSGi先锋-林昊
    Python源码剖析-陈儒
    Silverlight 2完美征程-李会军
    Windbg专家-熊力(RSS)
    Windows系统技术研究-潘爱民
    冒号和他的学生们-郑晖
    大道至简-周爱民
    开源网格GIS实验田-高昂
    搜索专家-车东
    测试专家-陈绍英
    移山之道-邹欣
    系统架构师-张宴
    设计模式-王翔
    走出软件作坊-阿朱
    软件调试-张银奎
    轻轻松松自动化测试-朱少民
    存档
    Csdn Blog version 3.1a
    Copyright © 武汉博文视点