自己非常喜欢《程序员》杂志,《程序员》杂志在一定程序上很能开阔我们的视野。因此,一直都想推荐给大家。

方便大家相互学习交流,本文转自《程序员》杂志

http://www.csdn.net/article/2013-08-20/2816644

————————————————————————————————————————————————————————————————————————————

美术馆问题 

假如有一个凹四边形的美术馆。我们需要在美术馆当中安排尽可能少的警卫,使得他们能观察到美术馆的所有角落。我们可以像图1那样安排两个警卫,但实际上,像图2那样安排一个警卫就够了。如果美术馆的形状更加复杂,很可能一个警卫永远不够,例如在图3所示的美术馆中,安排两个警卫是必需的。如果美术馆的形状更加复杂,例如图4那样,那么最少需要安排多少个警卫呢?这就是著名的美术馆问题(Art Gallery Problem):给定一个多边形,确定最少需要在多边形内放置多少名警卫,才能让他们的视野覆盖整个多边形。 

有时,虽然多边形非常复杂,顶点数非常多,但放置一个警卫就够了(如图5所示)。当然,也有一些构造多边形的定式,可以充分利用顶点的数目,产生尽可能多的偏僻角落,从而增加所需警卫的数目。例如,利用图6所示的方法便能得出一系列含有n=3k个顶点的多边形,使得里面至少要放置k个警卫才行。如果顶点数是3k+1或者3k+2的话,我们可以先作出顶点数为3k的多边形,再把剩下的顶点当作“废”的顶点添加进去,这仍然可以让所需警卫的数量达到k。由此可知,对于任意顶点数n,我们都能找到一个至少需要⌊n/3」个警卫的多边形(其中⌊x⌋表示不超过x的最大整数)。

1975年,Václav Chvátal证明了一个非常经典的结论:对于任意一个含有n个顶点的多边形,放置⌊n/3⌋个警卫永远是足够的。不过,Václav Chvátal给出的证明过程非常复杂。1978年,Steve Fisk给出了另一种非常精妙的证明,整个证明过程只有五行。

让我们先来看看Fisk的证明思想。多边形中的视野问题不好处理,但三角形中的视野问题是很简单的:站在其中的任意一个位置都能观察到这个三角形中的每一个点(究其原因,是因为三角形永远是一个凸图形)。我们可以作出多边形的若干条对角线,把整个多边形划分成一个个的三角形,如图7所示。在每一个三角形里面放一个警卫,就能看守整个多边形了。不过,把警卫放在每个三角形的正中间也实在是有些亏。为什么不把警卫放在多边形的对角线上呢?多边形的每一条对角线都是两个三角形的公共边,站在它上面的每一个警卫都能同时看守两个三角形。这样一来,我们便能大大减少所需警卫的数目。其实,把警卫放在边上还是很亏。为什么不把警卫放在多边形的顶点上呢?这样的话,每个警卫都能看遍顶点交汇于此的所有三角形了。这就是Fisk的证明思路。

把任意一个顶点数为n的多边形分割成小三角形。将各顶点染成红、绿、蓝三种颜色,使得每一个三角形的三个顶点正好是三种不同的颜色(如图8)。不妨假设,在三种颜色的顶点中,红色的顶点最少。那么,红色顶点的数目一定小于等于n/3。现在,在每个红色顶点处各安排一名警卫。由于每个三角形都有一个红色的顶点,所以每个三角形里的每一个点都保证能被看到。这样,我们就找到了一个大小不超过⌊n/3⌋的点集,它们足以看到多边形内的每一个点。

Fisk给出的证明非常巧妙,不过里面略去了很多细节问题。为什么存在满足要求的染色方案?更进一步地问,为什么我们可以把多边形分割成一个个的小三角形?这其实都是需要证明的。

多边形的三角剖分

大家或许会说,一个多边形能被分割成若干个小三角形,这不是一件很显然的事情吗?比方说,随便找一条把多边形分成两部分的对角线,然后递归地对每一部分进行分割,直到最终变成了一堆三角形为止。这个推理过程看上去很合理,但却有一个不易察觉的漏洞:你怎么敢肯定,存在一条把多边形分成两部分的对角线呢?令人吃惊的是,证明这一点并不容易。

1975年,Meisters给出了一个证明。首先,选取一个横坐标最小的顶点。由于其他点都位于这个点的右侧(最多和它在同一竖直线上),因此这一定是一个向外凸的点。不妨把这个点记作Pi,把与它相邻的两个点记作Pi-1和Pi+1。如果Pi-1和Pi+1的连线与多边形的每一条边都不相交,这说明Pi-1Pi+1完全在多边形的内部,它就是一条把多边形分成两块的对角线,如图9所示。如果Pi-1和Pi+1的连线穿过了至少一条多边形的边,这就说明Pi-1Pi+1有一部分跑到了多边形外面去了,它不是一条可取的对角线。那该怎么办呢?容易看出,三角形PiPi-1Pi+1里面一定会包含其他的顶点。我们从中选择一个距离直线Pi-1Pi+1最远的点,不妨把它记作R,如图10所示。那么,过R作Pi-1Pi+1的平行线,其左侧就不会再有别的点了。于是,PiR就是一条安全的连线,它将会把多边形分成两部分。

顺时针(或者逆时针)给出多边形各个顶点的坐标,我们可以用O(n)的时间找出一条把多边形分成两部分的对角线。找出最左边的那个顶点Pi需要O(n)的时间,判断Pi-1Pi+1是否与多边形各边发生相交也需要O(n)的时间。如果确实有相交的情况发生,那么我们就枚举除了Pi、Pi-1、Pi+1之外的每一个顶点,筛选出位于三角形Pi、Pi-1、Pi+1以内的点,并找出其中哪个点和Pi-1、Pi+1所构成的三角形面积最大。根据三角形的面积公式,这个点就一定是到Pi-1、Pi+1距离最远的点了。这都可以在O(n)的时间里完成。

之后,整个多边形将会被分成两个小多边形,每个小多边形的顶点数都比原多边形更少一些。如果其中一个小多边形有k个顶点(注意到k一定是大于等于3的),那么另一个小多边形将会有n-k+2个顶点(因为这两个小多边形会有两个公共顶点),但考虑到k≥3,后一个多边形的顶点数仍然是一个比n小的数。递归地对两个小多边形进行细分,便能得到最终的三角剖分方案了。我们可以用数学归纳法来证明,整个多边形最终将会被n–3条对角线分成n–2个小三角形。根据归纳假设,其中一个小多边形将会被k–3条对角线分成k–2个小三角形,另一个小多边形则会被n-k–1条对角线分成n-k个小三角形。把两个小多边形合在一起,于是就有(k–3)+(n-k–1)+1=n–3条对角线(注意到两个小多边形的粘合处也是大多边形的一条对角线),以及(k–2)+(n-k)=n–2个小三角形。

因此,在递归地把各个小多边形都分到底的过程中,我们总共会找出O(n)条对角线,每次找到一条对角线都需要花费O(n)的时间,因而总的复杂度就是O(n²)。当然,考虑到后面需要处理的多边形越来越小,因而实际的时间复杂度可能会更低。运气最好时,每条对角线都把多边形分成了顶点数相等的两个小多边形,则总的复杂度就是O(n·log n)。

1977年,Lee和Preparata提出了一种利用“单调多边形”进行快速三角剖分的方法,在最坏情况下的复杂度也是O(n·log n)。之后的很长一段时间里,人们都在思考这样一个问题:是否存在复杂度低于O(n·log n)的三角形剖分算法。1988年,Tarjan和Van Wyk提出了一种O(n·log log n)的算法,从而给出了一个肯定的回答。1991年,Bernard Chazelle用将近40页的篇幅描述了一种O(n)的算法,虽然算法本身极其复杂,但为三角形剖分的复杂度问题画上了一个句号。

形形色色的变形问题

接下来,大家可以自己尝试着把图7中的各个顶点染成三种颜色,使得相邻顶点的颜色都不相同。稍作尝试,你会发现这并不困难。随便选择一个三角形,把它的三个顶点染成三种不同的颜色,然后像玩扫雷游戏一样顺推下去,得出其他所有点的颜色。每一次,我们总是从一个已染好颜色的三角形出发,迈过一条对角线,来到一个新的三角形里。在这个新的三角形中,只有一个顶点还没有颜色,我们只需要给它染上与其他两个顶点都不同的颜色即可。这样,我们就用掉了一条对角线,处理了一个新的三角形,给一个新的顶点染上了颜色。刚开始,我们没有跨过任何对角线,手中只有一个处理过的三角形,只有三个顶点被染了颜色。等到所有n–3条对角线都用掉了之后,所有n–2个三角形也就全部处理完了,所有n个顶点也就都有颜色了。整个过程相当于一次深度优先搜索,时间复杂度为O(n)。

最后,看看哪种颜色的顶点最少,那么就把警卫安放在这种颜色的顶点上。因而,我们不但成功地证明了⌊n/3⌋个警卫的充分性,还给出了一种安放这⌊n/3⌋个警卫的算法来。

有趣的是,如果多边形里面有“洞”(比方说房间里的立柱),如图11所示,此时三角剖分的算法和染色环节的算法都会失效。在三角剖分时,我们总是假设每条对角线都能把多边形分成两块,然而对于有洞的多边形来说,这一点并不总是成立的。我们之所以能顺利地完成染色环节,也是因为多边形里面没有洞。在一个正常的多边形中,每一条对角线都会把整个多边形断成两部分,因此绝不会出现从某处出发连续跨过若干条对角线最后又回到出发点的情况;但在一个有洞的多边形中,我们有可能走着走着就回到了已处理过的地方,新三角形的第三个顶点有可能已被染过色了,进而导致矛盾产生。

不过,我们可以把一个有洞的多边形看作是一个普通的多边形。假如一个多边形里面有h个洞,那么我们就可以像图12那样,在图中增加2h个顶点,把这h个洞都和外面连通,使得整个图变成拥有n+2h个顶点的多边形。我们立即证明了,一个顶点数为n的、含有h个洞的多边形一定能被⌊(n+2h)/3⌋个警卫完全看守住。

另一方面,人们已找到了一系列的多边形,它们都需要至少⌊(n+h)/3⌋名警卫才能被完全看住。但人们怎么也找不到所需警卫更多的多边形了。于是,人们猜想,⌊(n+h)/3⌋个警卫是否也一定足够了呢?1986年,Shermer成功证明了h=1的情形,但这种证明方法很难推广到h>1的情形。1991年,Hoffmann、Kaufmann和Kriegel终于证明了h>1的情形,从而完美地解决了允许中间有洞的美术馆问题。

除了给多边形挖洞以外,人们还研究了很多美术馆问题的变形。1983年,O'Rourke证明了,如果允许每一名警卫都沿着一条线段来回移动,那么⌊n/4⌋名警卫一定是足够的。1984年,Kahn、Klawe和Kleitman证明了,如果多边形的每条边都是水平的或者竖直的,那么⌊n/4⌋名警卫一定是足够的。2003年,Michael和Pinciu证明了,如果要求每个警卫都能被至少一个别的警卫看到,那么⌊(3n-1)/7⌋名警卫一定是足够的。并且,我们总能构造一些特殊的多边形,让所需的警卫数达到刚才这些上界。这说明,上面这些结论都已不能再改进了。

不过,这些结论都只提供了所需警卫的上界。对于一些特定的多边形来说,我们远远用不到这么多警卫,就像图5那样。给出一个特定的多边形后,如何求出最少所需的警卫数目?1986年,D. Lee和A. Lin证明了,这个问题是NP-hard的。这是计算几何中一个非常重要的结论。

还有很多与线路规划相关的问题也是NP-hard的,例如著名的旅行商问题(Travelling Salesman Problem):给出平面上的若干个点,求出经过每个点恰好一次并且回到出发点的最短路线。不可思议的是,如果把美术馆问题和旅行商问题“杂交”一下,得出的问题竟然是有多项式解法的。1988年,Chin和Ntafos提出了这么一个问题:给定一个多边形,如何找出多边形内的一条最短回路,使得途中能看到多边形里面的每一个点?1995年,Nilsson给出了一个O(n6)的算法,从而证明了上述问题能在多项式的时间内解决。

此外,Chin和Ntafos还考虑了这样一个问题:给定一个多边形,以及多边形边界上的一点(相当于美术馆的大门),如何找出一条从该点出发,最后又回到该点的路线,使得途中能够看到多边形里面的每一个点?两人在1991年给出了一个O(n4)的算法。1997年,Carlsson、Jonsson和Nilsson指出了这个算法中的一处漏洞,并提出了一种修正的方案。然而,两年之后,Tan、Hirata和Inagaki发现,这个修正方案本身也是有问题的。在同一篇论文中,他们给出了一种O(n4)的算法,目前看来应该是正确的。

人们提出这些问题,固然是因为有其实际应用价值——安装最少的摄像头来监视超市里的每个角落,使用为数不多的点光源照亮整个房间,布置数量有限的激光扫描器来生成室内三维地图,设计最优的摄像机轨道铺设方案,让机器人自动寻找最佳的进楼搜救路线等。然而更关键的则是这些问题本身的吸引力。美术馆问题本身拥有极其简单的描述和极其直观的解释,解决起来则需要综合应用几何、图论和计算机科学等诸多领域中的知识和技巧。这无疑是计算几何中最具魅力的问题之一。

作者顾森,网名Matrix67,数学爱好者。2005年开办数学博客http://www.matrix67.com,至今已积累上千篇文章,有上万人订阅。新书《思考的乐趣》已在图灵公司出版。

《程序员》 -- 计算几何的魅力:美术馆问题相关推荐

  1. 最萌办公室采访 | 网易程序员灵魂大拷问(文末有福利)

    一直以来,大家对程序员的固有印象是什么? 低调沉闷游戏宅? 不善言辞没情调? NO!  NO!  NO! 网易云信对8位优秀程序员进行灵魂拷问 一个短片让你感受到程序员的迷人魅力 精彩抢先看 如何用一 ...

  2. 潜伏研发群一个月,我发现了程序员不为人知的秘密!这也太可爱了吧

    文章来源于网易号丨InfoQ:Q妹,文章未删改 在公司研发群潜伏了一个月后,Q妹发现了一些不为人知的秘密,这群程序员着实让人上头- (一) 他们没有<吐槽大会>中码农庞博 那般能说会道,高 ...

  3. 潜伏研发群一个月,我发现了程序员不为人知的秘密

    一 在公司研发群潜伏了一个月后 Q妹发现了一些不为人知的秘密 这群程序员着实让人上头- 他们没有<吐槽大会>中码农庞博 那般能说会道,高大帅气 相反,有着鲜明个性且具有辨识度的他们 是一群 ...

  4. 1024,不讲技术,来一套程序员续命操~

    今天有一群很可爱的人过节, 他们是一种神奇的生物:喝的是咖啡,挤的是代码, 每天的工作就是让我们所处的这个世界, 再美好一点,再便利一点,再酷一点, 享受他们的工作成果,我们很快乐, 而这份快乐,常常 ...

  5. 数学很差的人能当程序员吗?

    [CSDN 编者按]作者在大学时代受<程序员>杂志的启发,从数学专业投身计算机编程,毕业后进入软件开发行业.过去9年,他去过大厂敲代码,也曾在创业公司带过团队,一直从事"下一代& ...

  6. 张口闭口就是焦虑,现在的程序员怎么了?

    [CSDN 编者按]在网上,我们经常会看到各类程序员的吐槽,学历焦虑.大厂焦虑充斥着手机屏幕,本文作者从这一角度出发,用质朴的文字告诉年轻一代程序员一个事实:慢慢走好编程每一步,不要害怕更不用着急,因 ...

  7. 程序员加班崩溃,过路外卖小哥主动帮忙改代码,网友直呼太暖了!

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 有人说,成年人的崩溃总在一瞬间. 近日,一段青岛外卖小哥帮程序员写代 ...

  8. 她,诗人拜伦之女,英国数学家,历史上第一位程序员

    作者丨吴军 来源丨大数据文摘 摘自丨<信息论> 人类使用机械处理信息的尝试是由两个看似不该有交集的英国人开启的. 在英格兰中部莱斯特郡的柯比-马洛里庄园,住着一对母女. 1816年初那个多 ...

  9. 某程序员吐槽:31岁小姐姐拒绝条件优越的大厂程序员,只因身高不足163cm,难道矮是原罪?...

    之前我们写过身高在相亲中的重要性,尤其男生的身高更是小姐姐们的重点考量标准之一,只是这个标准真的这么重要吗? 一个阿里程序员吐槽:给妹子介绍男友,妹子身高161cm,比例曲线都很好,脸中等偏上,学历工 ...

最新文章

  1. Cisco路由器的Flash和NVRAM
  2. doctype声明的意义
  3. python实现胶囊网络_胶囊网络 -- Capsule Networks
  4. java多层panel,java-在h:panelGrid中具有多个子组件的自定义Facelets-Tag
  5. 基于JAVA+SpringBoot+Mybatis+MYSQL的贷款审批系统
  6. Rainbond 5.1.3 发布,快速部署和运维 Spring Cloud 集群
  7. 【PL/SQL】测试函数时,日期参数的输入格式
  8. git的丰富实用经验
  9. 为什么计算机报名无法选择福建,2020年9月福建计算机考试如何报名
  10. html中radio实现互斥
  11. talib 安装的问题
  12. 计算机网络6版,计算机网络教程(第6版)
  13. 三相逆变器双pi控制器参数如何调节_SPMSM控制:传统PI电流环参数的整定
  14. 上海是怎么错失这些年的互联网机遇的?
  15. 2019年成功与失败的危机公关案例分析
  16. Win10自带杀毒功能如何打开
  17. 带bitlocker解密的pe_Win10使用BitLocker加密U盘|Win10自带BitLocker加密U盘
  18. 快速提高意志力的方法--自控力
  19. SumatraPDF与VSCode反向搜索配置
  20. Redis6课程大纲

热门文章

  1. 百家号发帖软件怎么发帖?使用视频教程
  2. @ControllerAdvice+@ExceptionHandler处理架构异常捕获
  3. COSMIC: COmmonSense knowledge for eMotion Identification in Conversations
  4. linux进程控制命令行,linux命令行学习(37):控制进程的方法
  5. 原创:是什么改变了我当初的模样?
  6. J2EE基础之易错小札
  7. python 自带库
  8. Jenkins第一节:linux安装jenkins以及解决各种常见问题
  9. 除了Confluence,还有哪些好用的文档管理软件?测评
  10. 第 23 章 H3C ICG(Information Communication Gateway)