第5章 问 题 求 解 在人类进行科学探索与科学研究的过程中,曾经提出过许多对科学发展具有重要影响的著名问题,如哥德巴赫猜想、庞加莱猜想、四色定理和哥尼斯堡七桥问题等,其中一些问题对计算机学科及其分支领域的形成和发展起到重要作用。另外,在计算机学科的研究工作中,为了便于理解计算机科学中有关问题和概念的本质,计算机科学家给出了不少反映该学科某一方面本质特征的典型实例,在这里称为计算机领域的典型问题。计算机领域典型问题的提出及研究,不仅有助于深刻地理解计算机学科中一些关键问题的本质,而且对学科的继续深入研究和发展具有十分重要的促进作用。本章将从图论问题、算法复杂性问题、机器智能问题、并发控制问题和分布式计算问题等进行分析。 5.1问题求解的一般过程 问题求解是一个发现问题、分析问题,最后导向问题目标与结果的过程,一般包括提出问题、明确问题、提出假设、检验假设4个基本步骤。欧拉回路问题就是一个典型的问题求解过程。 18世纪中叶,东普鲁士有一座哥尼斯堡(Konigsberg)城,城中有一条贯穿全市的普雷格尔(Pregol)河,河中央有座小岛,叫奈佛夫(Kneiphof)岛,普雷格尔河的两条支流环绕其旁,并将整个城市分成北区、东区、南区和岛区4个区域,全城共有7座桥将4个城区相连起来,如图5.1所示。 图5.1哥尼斯堡七桥地理位置 示意图 当时该城市的人们热衷一个难题: 一个人怎样不重复地走完7桥,最后回到出发地点?即寻找走遍这7座桥,且 每座桥只许走过一次,最后又回到原出发点的路径。所有试验者都没有解决这个难题。1736年,瑞士数学家列昂纳德·欧拉(L.Euler)发表图论的首篇论文,论证了该问题无解,即从一点出发不重复地走遍7桥,最后又回到原来出发点是不可能的。后人为了纪念数学家欧拉,将这个难题称为“哥尼斯堡七桥问题”。 图5.2哥尼斯堡七桥问题示意图 为了解决哥尼斯堡七桥问题,欧拉用4个字母A、B、C和D代表4个城区,并用7条线表示7座桥,如图5.2所示,图中,只有4个点和7条边,这样做是基于该问题的本质考虑,抽象出问题最本质的东西,忽视问题非本质的东西(如桥的长度等),从而将哥尼斯堡七桥问题抽象成为一个数学问题,即经过图中每条边一次且仅一次的回路问题。欧拉在论文中论证了这样的回路是不存在的,后来,人们把有这样回路的图称为欧拉图, 即包含有经过所有边的简单生成回路的图称为欧拉图。 欧拉在论文中将问题进行了一般化处理,即对给定的任意一个河道图与任意多座桥,判定是否可能每座桥恰好走过一次(不一定回到原出发点),并用数学方法给出了下列3条判定规则。 ① 如果通奇数座桥的地方不止两个(>2),满足要求的路线是找不到的。 ② 如果只有两个地方通奇数座桥,可以从这两个地方之一出发,找到所要求的路线。 ③ 如果没有一个地方是通奇数座桥的,则无论从哪里出发,所要求的路线都能实现。 上述3条是欧拉通路的判定规则。 欧拉回路的判定规则如下。 图中所有地方都通偶数座桥(图中所有节点的边均为偶数)。根据判定规则可以得出,任一连通无向图在无向图中,每个节点连边的条数就是该节点的度数。 存在欧拉回路的充分必要条件是图的所有顶点均有偶数度。 有向欧拉通路的判定规则如下。 ① 图连通。 ② 除两个节点外,其余节点的入度=出度。 ③ 一个节点的入度比出度大1,一个节点的入度比出度小1,或者所有节点的入度=出度。 有向欧拉回路的判定规则如下。 ① 图连通。 ② 所有节点的入度=出度。 欧拉的论文为图论的形成奠定了基础。今天,图论已广泛地应用于计算机科学、运筹学、信息论和控制论等学科中,并已成为人们对现实问题进行抽象的强有力的数学工具。随着计算机科学的发展,图论在计算机科学中的作用越来越大,同时,图论本身也得到了充分的发展。 5.2计算机领域的典型问题 5.2.1图论问题 1. 哈密尔顿回路问题 在图论中除了欧拉回路以外,还有一个著名的“哈密尔顿回路问题”。19世纪爱尔兰数学家哈密尔顿(Hamilton)发明了一种叫作周游世界的数学游戏。它的玩法是: 给定一个正十二面体,它有20个顶点,把每个顶点看作一个城市,把正十二面体的30条棱看成连接这些城市的路。请找一条从某城市出发,经过每个城市恰好一次, 并且最后回到出发点的路线。我们把正十二面体投影到平面上,在图5.3中标出了一种走法,即从城市1出发,经过2,3,…,20,最后回到1。 “哈密尔顿回路问题”与“欧拉回路问题”看上去十分相似,然而又是完全不同的两个问题。“哈密尔顿回路问题”是访问每个节点一次,而“欧拉回路问题”是访问每条边一次。对图是否存在“欧拉回路”前面已给出充分必要条件,而对图是否存在“哈密尔顿回路”至今仍未找到满足该问题的充分必要条件。 2. 中国邮路问题 我国著名数学家管梅谷教授在1960年提出了一个有重要理论意义和广泛应用背景的问题,被称为“中国邮路问题”。邮递员要把信送往各地点,由于送信地点多,道路不好走,还要绕过楼房,出发前需要设计一条送信路线,从邮局出发不但把信送到每 个地点,而且路线不重复,最后回到邮局。这一问题可以表示为图5.4,其中,“·”代表送信地点,空白方格“□”表示两个送信地点之间必须经过的区域,行走时不能走对角。该问题归结为图论问题就是: 给定一个连通无向图(没有孤立的点),每条边都有非负的确定长度,求该图的一条经过每条边至少一次的最短回路。 图5.3周游世界示意图 图5.4中国邮路问题 对于这一问题,可以采用以下几步来解决。 ① 图论建模。由于街道是双向通行的,可以把它看成是赋权无向连通图,将路口抽象为点,街道抽象为边,街道的长度就是每条边的权值,问题转化为在图中求一条回路,使得回路的总权值最小。最理想的情况: 若图中有欧拉回路,即可通过所有的边,因此任何一个欧拉回路即为此问题的解。 ② 若无向图G只有两个奇点在图论中,无向图G中,与顶点v关联的边的数目(环算两次),称为顶点v的度或次数,把度为奇数的顶点称为奇点。Vi和Vj,则有从Vi到Vj的欧拉迹(即欧拉通路,通过图中每条边一次且仅一次,并且过每一顶点的通路),从Vj回到Vi则必须重复一些边,使重复边的总长度最小,转化为求从Vi到Vj的最短路径。算法: 找出奇点Vi与Vj之间的最短路径P; 令G′=G+P; G′为欧拉图,G′的欧拉回路即为最优邮路。 ③ 一般情况,奇点数大于2时,邮路必须重复更多的边。Edmonds算法也叫匈牙利算法,由匈牙利数学家Edmonds于1965年提出,因而得名。匈牙利算法是基于Hall定理中充分性证明的思想,它是部图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最大匹配的算法。(匈牙利算法)思想步骤: 求出G中所有奇点之间的最短路径和距离; 以G的所有奇点为节点(必为偶数),以它们之间的最短距离为节点之间边的权值,得到一个完全图G1; 将G的匹配图M中的匹配边(Vi,Vj)写成Vi与Vj之间最短路径经过的所有边的集合Eij; 令G′=GU{Eij|(Vi,Vj)∈M},则G′是欧拉图,求出最优邮路。 3. 网络爬虫 网络爬虫是一个自动提取网页的程序。整个互联网中所有网页构成的图中每个网页URL作为一个节点,网页链接构成了节点之间的边,整个万维网可以看作一个图。网络爬虫涉及图论中经典的搜索算法。 1) 广度优先搜索 广度优先搜索策略是指在网页获取过程中,在完成当前层次的搜索抓取后,再进行下一层次的搜索抓取。目前,为覆盖尽可能多的网页,一般使用广度优先搜索方法。也有很多研究将广度优先搜索策略应用于聚焦爬虫中,其基本假设是初始页面与其在一定链接距离内的网页具有主题相关性的概率很大。 2) 深度优先搜索 深度优先搜索策略从起始网页URL开始进入,分析这个网页中的其他超链接URL,选择一个URL再进入。如此一个链接一个链接地抓取下去,直到处理完一条路线之后再选择处理下一条路线。 5.2.2算法复杂性问题 1. 汉诺塔问题 传说在古代印度的贝拿勒斯神庙里安放了一块黄铜座,座上竖有3根宝石柱子。在第一根宝石柱上,按照从小到大、自上而下的顺序放有64个直径大小不一的金盘子,形成一座金塔,如图5.5所示,即所谓的汉诺塔(又称梵天塔)。天神让庙里的僧侣们将第一根柱子上的64个盘子借助第二根柱子全部移到第三根柱子上,即将整个塔迁移,同时定下如下3条规则。 图5.5汉诺塔问题示意图 ① 每次只能移动一个盘子。 ② 盘子只能在3根柱子上来回移动,不能放在别处。 ③ 在移动过程中,3根柱子上的盘子必须始终保持大盘在下,小盘在上。 据说当这64个盘子全部移到第三根柱子上后,世界末日就要到了。 汉诺塔问题是一个典型的用递归方法来解决的问题。递归是计算机学科中的一个重要概念,它是将一个较大的问题归约为一个或多个子问题的求解方法,这些子问题比原问题简单,但在性质上与原问题相同。 按照这种思想要解决64个盘子的汉诺塔问题可以转化为63个盘子的汉诺塔问题。以此类推,63个盘子的汉诺塔求解问题可以转化为62个盘子的汉诺塔求解问题,62个盘子的汉诺塔求解问题又可以转化为61个盘子的汉诺塔求解问题,直到1个盘子的汉诺塔求解问题。再由1个盘子的汉诺塔的解求出2个盘子的汉诺塔……直到解出64个盘子的汉诺塔问题。 若从左到右的柱子依次为A、B和C。移动时首先把上面n-1个盘子移动到柱子B上,然后把最大的一块放在C上,最后把B上的所有盘子移动到C上,由此可以得出移动n个盘子的次数H(n)表达式: H(1)=1(n=1) H(n)=2H(n-1)+1(n>1) (5.1) (5.2) 那么就能得到H(n)的一般式(其中n为盘子数目): H(n)=2n-1(n>0)(5.3) 因此,要完成64个盘子的汉诺塔的搬迁,需要移动盘子的次数为264-1=18446744073709551615次。如果每次移动花费一秒,则移完这些盘子需要5845.54亿年以上,而地球存在至今不过45亿年,太阳系的预期寿命据说是数百亿年。真的过了5845.54亿年,不用说太阳系和银河系,至少地球上的一切生命,连同梵天塔和庙宇等,都早已经灰飞烟灭。 2. 旅行商问题 旅行商问题(Traveling Salesman Problem,TSP)是威廉·哈密尔顿(W.R.Hamilton)爵士和英国数学家克克曼(T. P.Kirkman)于19世纪初提出的一个数学问题,这是一个典型的NP完全性问题。其大意是: 有若干个城市,任何两个城市之间的距离都是确定的,现要求一旅行商从某城市出发,必须经过每个城市且只能在每个城市逗留一次,最后回到原出发城市。问如何事先确定好一条最短的路线,使其旅行的费用最少? 人们在考虑解决这个问题时,首先想到的最原始的一种方法是: 列出每条可供选择的路线(即对给定的城市进行排列组合),计算出每条路线的总里程,最后从中选出一条最短的路线。假设现在给定4个城市分别为A、B、C和D,各城市之间的距离为已知数,如图5.6和图5.7所示。从图中可以看到,可供选择的路线共有6条,可以很快选出一条总距离最短的路线。 当城市数目为n时,那么组合路径数则为(n-1)!。很显然,当城市数目不多时要找到最短距离的路线并不难,但随着城市数目的不断增大,组合路线数将呈阶乘级急剧增长,以至达到无法计算的地步,这就是所谓的“组合爆炸问题”。假设现在城市的数目增为20个,组合路径数则为(20-1)!≈1.216×1017,如此庞大的组合数目,若计算机以每秒检索1000万条路线的速度计算,也需要花上386年的时间。 图5.6城市交通图 图5.7组合路径图 3. 算法复杂性分析与难解性问题 算法分析是计算机科学的一项主要工作。为了进行算法比较,必须给出算法效率的某种衡量标准。假设M是一种算法,并设n为输入数据的规模,实施M所占用的时间和空间是衡量该算法效率的两个主要指标。时间由“操作”次数衡量,比如对于排序和查找,需要对比较次数计数; 空间由实施该算法所需的最大内存来衡量。 算法M的复杂性可表示为一个函数f(n),它给出了输入数据规模为n时运行该算法所需的时间与存储空间。执行一个算法所需存储空间通常就是数据规模的倍数或平方,因此,一般“复杂性”主要指算法的运行时间。 对于时间复杂性函数f(n),它通常不仅与输入数据的规模有关,还与特定的数据有关。例如,在一篇英文短文中查找第一次出现的3个字母的单词W。那么,如果W为定冠词the,则W很可能在短文的开头部分出现,于是f(n)值将会比较小; 如果W是单词axe,则W可能不会在短文中出现,则f(n)会很大。因此,要考虑在适当的情况下,求出复杂性函数f(n)。在复杂性理论中研究得最多的两种情况如下。 ① 最坏情况。对于任何可能的输入,f(n)的最大值。 ② 平均情况。f(n)的期望值。 显然,M的复杂性f(n)随着n的增大而增大。因此需要考察的是f(n)的增长率,这常常由f(n)与某标准函数相比较而得,例如, log2n、nlog2n、n3、2n、cn等,都可用作标准函数,其中对数函数log2n增长最慢,指数函数2n增长最快,而多项式函数cn的增长率随其系数c的增大而变快。将复杂性函数与一个标准函数相比较的一种方法是利用O标记,这里给出它的定义。 设f(x)与g(x)为定义于R或R的子集上的任意两个函数,我们说“f(x)与g(x)同阶”,记作: f(x)=O(g(x))(5.4) 如果存在实数k和正常数C,使得对于所有的x>k,有 |f(x)|≤C|g(x)|(5.5) 如n2+n+1≈O(n2),该表达式表示,当n足够大时表达式左边约等于n2。 汉诺塔问题中需要移动的盘子次数为h(n)=2n-1,因此,该问题的算法时间复杂度为O(2n)。 一个算法的时间复杂度大于多项式(如指数函数)时,算法的执行时间将随n的增加而急剧增长,以致即使是中等规模的问题也不能求解出来。于是在计算复杂性中,将这一类问题称为难解性问题,也称“NP难解问题”。为了更好地理解计算及其复杂性的有关概念,我国学者洪加威曾经讲了一个被人称为“证比求易算法”的童话,用来帮助读者理解计算复杂性的有关概念。 很久以前,有一个年轻的国王,名叫艾述。他酷爱数学,聘请了当时最有名的数学家孔唤石当宰相。邻国有一位聪明美丽的公主,名字叫秋碧贞楠。艾述国王爱上了这位邻国公主,便亲自登门求婚。 公主说: “你如果向我求婚,请你先求出48 770 428 433 377 171的一个真因子,一天之内交卷。”艾述听罢,心中暗喜,心想: 我从2开始,一个一个地试,看看能不能除尽这个数,还怕找不到这个真因子吗?艾述国王十分精于计算,他一秒钟就能算完一个数。可是,他从早到晚,共算了三万多个数,最终还是没有结果。国王向公主求情,公主将答案相告: 223 092 827是它的一个真因子。国王很快就验证了这个数确实能被48 770 428 433 377 171除尽。 公主说: “我再给你一次机会,如果还求不出,将来你只好做我的证婚人了”。国王立即回国,召见宰相孔唤石,大数学家在仔细地思考后认为这个数为17位,如果这个数可以分成两个真因子的乘积,则最小的一个真因子不会超过9位。于是他给国王出了一个主意: 按自然数的顺序给全国的老百姓每人编一个号发下去,等公主给出数目后,立即将它们通报全国,让每个老百姓用自己的编号去除这个数,除尽了立即上报,赏黄金万两。于是,国王发动全国上下的民众,再度求婚,终于取得成功。 在“证比求易算法”的故事中,国王最先使用的是一种顺序算法,其复杂性表现在时间方面,宰相后来提出的是一种并行算法,其复杂性表现在空间方面。直觉上,我们认为顺序算法解决不了的问题完全可以用并行算法来解决,甚至会想,并行计算机系统求解问题的速度将随着处理器数目的不断增加而不断提高,从而解决难解性问题,其实这是一种误解。原因是: 当将一个问题分解到多个处理器上解决时,算法中不可避免地存在必须串行执行的操作,因此会大大地限制并行计算机系统的加速能力。下面,用阿姆达尔(G.Amdahl)定律来说明这个问题。 设f为求解某个问题的计算中存在的必须串行执行的操作占整个计算的百分比,p为处理器的数目,Sp为并行计算机系统最大的加速能力(单位: 倍),则 Sp=1f+1-fp(5.6) 设f=1%,p→∞,则Sp=100。这说明即使在并行计算机系统中有无穷多个处理器,解决这个串行执行操作仅占全部操作1%的问题,其解题速度与单处理器的计算机相比最多也只能提高100倍。因此,对难解性问题而言,单纯提高计算机系统的速度远远不够,降低算法复杂度的数量级才是最关键的。 国王有众多百姓的帮助,求亲成功是自然的事。但是,如果换成是一个平民百姓的小伙子去求婚,那就困难了。不过,小伙子可以从国王求亲成功所采用的并行算法中得到一个启发,那就是: 他可以随便猜一个数,然后验证这个数。当然,这样做成功的可能性很小,不过,万一小伙子运气好猜着了呢?由于一个数和它的因子之间存在一些有规律的联系,因此,数论知识水平较高的人猜中的可能性就大。 这个小伙子使用的随机猜算法叫作非确定性算法,这样的算法需要有一种目前并不存在的非确定性计算机才能运行,其理论上的计算模型是非确定性图灵机。 在计算复杂性的研究中,所有可以在多项式时间内求解的问题称为P类问题,而所有在多项式时间内可以验证的问题称为NP类问题,P类问题采用的是确定性算法,NP类问题采用的是非确定性算法,由于确定性算法是非确定性算法的一个特例,因此P