问题:

    星期五的晚上,一帮同事在希格玛大厦附近的“硬盘酒吧”多喝了几杯。程序员多喝了几杯之后谈什么呢?自然是算法问题。有个同事说:“我以前在餐馆打工,顾客经常点非常多的烙饼。店里的饼大小不一,我习惯在到达顾客饭桌前,把一摞饼按照大小次序摆好——小的在上面,大的在下面。由于我一只手托着盘子,只好用另一只手,一次抓住最上面的几块饼,把它们上下颠倒个个儿,反复几次之后,这摞烙饼就排好序了。我后来想,这实际上是个有趣的排序问题:假设有n块大小不一的烙饼,那最少要翻几次,才能达到最后大小有序的结果呢?”

你能否写出一个程序,对于n块大小不一的烙饼,输出最优化的翻饼过程呢?


(1)基本步骤

1、最上面的和下面“未选出的最大”的做一次翻转——————》最大的跑到上面了

2、最大的和下面未初始化的第一个翻转——————》最大的跑到了最下面

3、重复以上过程,直到全部有序


(2)如何改进以上的次数?

关于一摞烙饼的排序问题我们可以采用递归的方式来完成。其间我们要做的是尽量调整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*算法的估计代价。所以,在给出一个合理的代价之前,我们所有的努力都只能是帮忙“加速”,而无法真正在多项式时间内解决问题。

(1.5.1.3)编程之美:一摞烙饼的排序相关推荐

  1. 编程之美1.3-翻烙饼问题

    问题:给定一组随机数字,使其从大到小排序. 要求:只能对数组做一种操作--翻转Arr[0]至Arr[n],其中n为大于0小于ArrLength的整数 解题思路:搜索树算法.递归遍历+减枝 详解: 1. ...

  2. 编程之美-一摞烙饼的排序方法整理

    [问题描述] [方法]

  3. 编程之美之一摞烙饼的排序1

    拿到这个问题, 第一反应是利用分治的算法思想, 每次把当前的最大的一块烙饼放到指定位置 ,这样的思想非常简单,实现也非常容易.但是这只是提供了,问题的一个可行解,看完书中的内容之后发现,题目中要求的是 ...

  4. 编程之美2.10:寻找数组中的最大值和最小值

    编程之美2.10: 对于一个有N个整数组成的数组,需要比较多少次才能把最大值和最小值找出来呢? 算法的思想是: 分而治之 测试数据:---------------------------------- ...

  5. 编程之美2.1 求二进制中1的个数

    最近一段的时间,一直在看编程之美之类的算法书籍,刚开始看编程之美,感觉到难度太大,有时候也不愿意去翻动这本书,不过,经过一段时间的修炼,我也彻底的喜欢上这本书了, 书中的算法涉及到很多方面,树,链表, ...

  6. 2017“编程之美”终章:AI之战勇者为王

    编者按:8月15日,第六届微软"编程之美"挑战赛在选手的火热比拼中圆满落下帷幕."编程之美"挑战赛是由微软主办,面向高校学生开展的大型编程比赛.自2012年起, ...

  7. Java 并发编程之美:并发编程高级篇之一-chat

    借用 Java 并发编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了.相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作 ...

  8. Java 并发编程之美:并发编程高级篇之一

    借用 Java 并发编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了.相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作 ...

  9. 《编程之美》1.3一摞烙饼的排序

    <编程之美>1.3一摞烙饼的排序       本文内容主要整理自http://yangguosheng.here.blog.163.com/blog/static/111479292201 ...

  10. 《编程之美——微软技术面试心得》一摞烙饼的排序初体验

    <编程之美>读书笔记:1.3 一摞烙饼的排序 问题: 星期五的晚上,一帮同事在希格玛大厦附近的"硬盘酒吧"多喝了几杯.程序员多喝了几杯之后谈什么呢?自然是算法问题.有个 ...

最新文章

  1. TensorFlow练习23: “恶作剧”
  2. 利用WinRAR命令行压缩文件或文件夹
  3. MyEclipse、eclipse代码自动补全
  4. java 方法 示例_Java集合checkedList()方法与示例
  5. FB接连出事儿?上亿用户记录在亚马逊云服务器上就公之于众了……下滑到第七?领英说苹果怎么就不受雇员欢迎了呢? | 极客头条...
  6. Linux 设备树知识点
  7. 微课|中学生可以这样学Python(5.8.1节):使用切片访问列表元素
  8. js在html中加文字走马灯特效,jQuery简单的文字跑马灯特效
  9. 帆软报表跨域之插件开发中Controller中定义的方法实现JSONP跨域
  10. c#转换XML文件和json对象
  11. 中心极限定理_中心极限定理和Python图解
  12. C语言 文件操作5--文件的常用函数
  13. hp t410微型计算机使用,HP 发表新款 t410 AIO Smart Zero 精简型电脑,仅需网络线即可作为电源驱动使用...
  14. cdlinux之U盘启动cdlinux破解wifi(计算机系破解无线密码的方式 就不使用wifi万能钥匙了)-- 没有发现无线网卡
  15. 36款免费可商用字体 附字体分享链接
  16. 一个简单的姓名生成器
  17. 拼音表大全图_语文汉语拼音教学指导方法|拼音教学游戏大全
  18. 国产旗舰手机价格泡沫严重,二手比新机更划算,要不然就买iPhone
  19. kaggle比赛tweet_sentiment_extraction,带你上0.71分数
  20. 爬取网易云音乐所有歌单信息

热门文章

  1. 自动化篇 - 为闲鱼制作一个客服机器人
  2. spring cloud 熔断hystrx
  3. latex数学公式(行内(间)公式标注/希腊字母/数学函数/配对括号/定理环境
  4. 机械键盘 酒精大法
  5. 2018前端面试题 css 部分
  6. Web前端面试指导(四十四):什么是响应式开发?
  7. 大商创小程序源码_小程序直播系统有哪些基本功能
  8. 以为女程序员的奋斗路程
  9. npm使用过程中的一些错误解决办法及npm常用命令
  10. 计算机信息与科学学院青协,武汉东湖学院计算机科学学院青年志愿者协会