# 17 | 蒙特卡洛与拉斯维加斯:有限时间内如何获得最优解? 数据给你一双看透本质的眼睛,这里是《数据分析思维课》,我是郭炜。 前面给你讲了回归、分类、聚类、关联等一些基础算法,其实如果有足够的时间和计算资源,我们其实能通过这些基础算法做出很多特别精确的预测和分析。 但实际我们在现实工作和生活中,没那么多的资源和时间来得到最佳的结果。那么在有限时间里,怎么样才能够获得比较好的计算答案,或者有没有好的办法能够在比较短的时间求得正确的答案呢?今天我就给你分享两个比较有代表性的算法:蒙特卡洛算法和拉斯维加斯算法。 ## 算法定义和场景 这两个算法的目标都是利用随机的方法来简化整体的算法过程,解决一些看上去我们没有办法通过正常算法解决的实际问题。 先给你讲讲蒙特卡罗算法,这个算法是在20世纪40年代,由S.M.乌拉姆和J.冯·诺伊曼首先提出来(对,就是那个世界上最早的通用电子计算机ENIAC创作者冯·诺伊曼)。 这个算法的名字由来其实很随意。那个时候,正值美国在第二次世界大战,乌拉姆和诺伊曼都是“曼哈顿计划”(美国原子弹计划)计划的成员,而第一台电子计算机ENIAC在发明后就被用于“曼哈顿计划”。在参与这个计划过程中,乌拉姆想到在计算机强大计算能力的帮助下,可以通过重复数百次模拟核实验的方式来对核裂变的各种概率变量进行演算,而不用实际进行那么多次实验。 冯·诺伊曼立即认识到这个想法的重要性并给予乌拉姆充分的支持,乌拉姆将这种统计方法用于计算核裂变的连锁反应,大大加快了这个项目的节奏。由于乌拉姆常说他叔叔在摩纳哥的赌城“Monte Carlo”输钱,他的同事Nicolas Metropolis戏称该方法为“蒙特卡罗”,这个名字也就沿用到了现在。 蒙特卡罗算法原理其实很简单,就是每次计算都尽量尝试找更好的结果路径,但不保证是最好的结果路径。用这样寻找结果的方法,无论何时都会有结果出来,而且给的时间越多、尝试越多,最终会越近似最优解。 举个例子,我们现在要用蒙特卡洛算法找到一个有500个苹果的筐里,最大的苹果。正常来讲,我们每次从筐中拿一个苹果A, 然后下一次再随机从筐中拿出另一个苹果B, 如果B比A大的话,就把A扔到另一个筐里,手里只拿着B。这样如果我们拿了500次的话,最后留在手里的一定是最大的那个苹果。 但如果我们的时间和资源不够拿500次苹果呢? 我们就可以用蒙特卡洛算法,无论我们选择多少次,每次手里依然保留比较大的苹果,直到资源不够让我们截止的时候,留在我们手上的苹果也是我们力所能及接近最大的。也就是说我们持续保留较好的答案,一直执行N次(N<500),最终拿到的一定近似正确解。**N越接近500,我们的苹果越接近最大的那个。**其实蒙特卡洛方法的理论基础就是我们前面讲过的[大数定律](https://time.geekbang.org/column/article/401316)。根据这个定律我们知道当随机事件发生的次数足够多时,发生的频率就会趋近于预期的概率。 和蒙特卡罗算法截然相反的另一种算法就是拉斯维加斯算法,它是在1979年[László Babai](https://en.wikipedia.org/wiki/L%C3%A1szl%C3%B3_Babai)在解决图同构问题的时候,针对蒙特卡罗算法弊病提出的。拉斯维加斯算法原理也很简单,就是**每次计算都尝试找到最好的答案,但不保证这次计算就能找到最好的答案,尝试次数越多,越有机会找到最优解。** 举个例子,假如有一把锁,给我100把钥匙,其中只有1把钥匙可以开锁。于是我每次随机抽1把钥匙去试,打不开就再换1把。我尝试的次数越多,打开锁的机会就越大。但在打开之前,那些错的钥匙都是没有用的。这个挨个尝试换钥匙开锁的算法,就是拉斯维加斯算法。 准确来讲,蒙特卡罗算法和拉斯维加斯算法其实并不是两种算法,而是两类算法的统称。 蒙特卡罗算法的基本思想是精益迭代,进行多次求解,最终让最后结果成为正确结果的可能性变高。而拉斯维加斯的算法是不断进行尝试,直到某次尝试结果让你自己满意,当然这个过程中也会一直产生你无法满意的随机值。 所以拉斯维加斯的算法效率通常比蒙特卡罗的算法低,但是最终得出的解一定是这个问题的正确解,当然也有可能无法得到问题的解。这两个算法之间的差别你可以通过下面的这个图表来进行区分。 ![](https://static001.geekbang.org/resource/image/21/85/2198b0209d69be129d39e4a63489e185.jpg?wh=1904x629) ## 蒙特卡洛和拉斯维加斯算法举例 刚刚从概念上带你了解了蒙特卡罗算法和拉斯维加斯算法,下面我实际给你举两个具体的例子来看看这两个算法的进一步运用。 首先我们来看看利用蒙特卡罗算法计算圆周率。 圆周率是通过n个多边形周长来推导计算出来的(可以参考附录),不过我们通过蒙特卡罗算法,也可以把圆周率计算出来。 **首先,我们构造一个正方形,里面套一个内切圆。** **然后,我们在这个正方形的内部随机打上1万个点。** **最后,根据它到中心点的距离来判断这个点是否落在圆的内部。**这个落在圆内部的概率其实和这个圆与正方形之间的面积有一个比例关系。也就是假设落在圆内的概率为P,则P=圆面积/正方形面积。`P=(π*R*R)/(2R*2R)= π/4`也就是 π=4P。 如果我们的这些点是足够均匀分布的,那么圆内的点应该占到整个正方形里面点的π/4,所以只要把这个概率P乘以4就是π的值。 ![](https://static001.geekbang.org/resource/image/c8/70/c8a31eba918409a37bdb9f1c2ebe2970.png?wh=500x501) 具体步骤如下。 1.将圆心设在原点,以R为半径作圆,则第一象限的1/4圆面积为`π*R*R/4`; 2.做该1/4圆的外接正方形,坐标为(0,0)(0,R)(R,0)(R,R),则该正方形面积为R\*R; 3.随机取点(X,Y),使得0<=X<=R并且0<=Y<=R,即点在正方形内; 4.通过公式 `X*X+Y*Y