算法设计与分析第7章概率算法.ppt
第第7章章 随机算法随机算法1学习要点学习要点了解随机算法的基本特征了解随机算法的基本特征理解产生伪随机数的算法理解产生伪随机数的算法掌握数值随机化算法的设计思想掌握数值随机化算法的设计思想 掌握舍伍德算法的设计思想掌握舍伍德算法的设计思想掌握拉斯维加斯算法的设计思想掌握拉斯维加斯算法的设计思想掌握蒙特卡罗算法的设计思想掌握蒙特卡罗算法的设计思想2概述概述 前面各章讨论的算法的每一个步骤都是确定的,而本前面各章讨论的算法的每一个步骤都是确定的,而本章讨论的随机算法允许算法在执行过程中随机地选择一下章讨论的随机算法允许算法在执行过程中随机地选择一下计算步骤。计算步骤。在许多情况下,一般算法比较复杂,性能较差,很多在许多情况下,一般算法比较复杂,性能较差,很多具有很好平均运行时间的算法,在最坏的情况下,却具有具有很好平均运行时间的算法,在最坏的情况下,却具有很坏的性能。由于随机性选择比最优选择省时间,因此引很坏的性能。由于随机性选择比最优选择省时间,因此引入随机化算法可以在很大程度上降低算法的复杂度。入随机化算法可以在很大程度上降低算法的复杂度。3很早以前就被人们所发现和利用。17世纪,人们就知道用事件发生的“频率”来决定事件的“概率”。19世纪人们用投针试验的方法来决定。高速计算机的出现,使得用数学方法在计算机上大量模拟这样的试验成为可能。4从从Buffon(蒲丰蒲丰)投针问题谈起投针问题谈起 567概述概述随机算法对所求解问题的同一个实例用同一随机算法求解随机算法对所求解问题的同一个实例用同一随机算法求解两次可能得到完全不同的效果。这两次求解所需要的时间,两次可能得到完全不同的效果。这两次求解所需要的时间,甚至所得到的结果都可能会有相当大的差别。甚至所得到的结果都可能会有相当大的差别。包括包括数值概率算法数值概率算法蒙特卡罗(蒙特卡罗(Monte Carlo)算法)算法拉斯维加斯(拉斯维加斯(Las Vegas)算法)算法舍伍德(舍伍德(Sherwood)算法)算法8数值概率算法常用于数值问题的求解。将一个问题的计算与某数值概率算法常用于数值问题的求解。将一个问题的计算与某个概率分布已经确定的事件联系起来,求问题的近似解。这类个概率分布已经确定的事件联系起来,求问题的近似解。这类算法所得到的往往是近似解,且近似解的精度随计算时间的增算法所得到的往往是近似解,且近似解的精度随计算时间的增加而不断提高。在许多情况下,要计算出问题的精确解是不可加而不断提高。在许多情况下,要计算出问题的精确解是不可能或没有必要的,因此可以用数值随机化算法得到相当满意的能或没有必要的,因此可以用数值随机化算法得到相当满意的解。解。蒙特卡罗算法用于求问题的准确解,但得到的解未必是正确蒙特卡罗算法用于求问题的准确解,但得到的解未必是正确的。蒙特卡罗算法以正的概率给出正解,求得正确解的概率的。蒙特卡罗算法以正的概率给出正解,求得正确解的概率依赖于算法所用的时间。算法所用的时间越多,得到正确解依赖于算法所用的时间。算法所用的时间越多,得到正确解的概率就越高。一般给定执行步骤的上界,给定一个输入,的概率就越高。一般给定执行步骤的上界,给定一个输入,算法都是在一个固定的步数内停止的。算法都是在一个固定的步数内停止的。随机算法的分类随机算法的分类9舍伍德算法总能求得问题的一个解,且所求得的解总是正确的。舍伍德算法总能求得问题的一个解,且所求得的解总是正确的。当一个确定性算法在最坏情况下的计算复杂性与其在平均情况当一个确定性算法在最坏情况下的计算复杂性与其在平均情况下的计算复杂性有较大差别时,可在这个确定性算法中引入随下的计算复杂性有较大差别时,可在这个确定性算法中引入随机性将它改造成一个舍伍德算法,消除或减少问题的好坏实例机性将它改造成一个舍伍德算法,消除或减少问题的好坏实例间的这种差别(精髓所在)。间的这种差别(精髓所在)。拉斯维加斯算法不会得到不正确的解。一旦用拉斯维加斯算法拉斯维加斯算法不会得到不正确的解。一旦用拉斯维加斯算法找到一个解,这个解就一定是正确解。但有时可能找不到解。找到一个解,这个解就一定是正确解。但有时可能找不到解。拉斯维加斯算法找到正确解的概率随着它所用的计算时间的增拉斯维加斯算法找到正确解的概率随着它所用的计算时间的增加而提高。对于所求解问题的任意实例,用同一拉斯维加斯算加而提高。对于所求解问题的任意实例,用同一拉斯维加斯算法反复对它求解,可以使求解失效的概率任意小。法反复对它求解,可以使求解失效的概率任意小。随机算法的分类随机算法的分类107.1随机数随机数117.1随机数随机数随机数在随机化算法设计中扮演着十分重要的角色。在现实计算机上随机数在随机化算法设计中扮演着十分重要的角色。在现实计算机上无法产生真正的随机数,因此在随机化算法中使用的随机数都是一定无法产生真正的随机数,因此在随机化算法中使用的随机数都是一定程度上随机的,即伪随机数。程度上随机的,即伪随机数。线性同余法是产生伪随机数的最常用的方法。由线性同余法产生的随线性同余法是产生伪随机数的最常用的方法。由线性同余法产生的随机序列机序列a0,a1,an,满足:,满足:(混合同余法)其中其中b 0,c 0,d m。d称为该随机序列的种子。如何选取该方法称为该随机序列的种子。如何选取该方法中的常数中的常数b、c和和m直接关系到所产生的随机序列的随机性能。这是随直接关系到所产生的随机序列的随机性能。这是随机性理论研究的内容,已超出本书讨论的范围。从直观上看,机性理论研究的内容,已超出本书讨论的范围。从直观上看,m应取应取得充分大,因此可取得充分大,因此可取m为机器大数。为机器大数。12d=1 种子m=11 b=6 c=0(1,6,3,7,9,10,5,8,4,2,1,6,3)13复杂一些的生成器复杂一些的生成器Multiple recursive generator14算法实现算法实现许多程序语言中都自带生成随机数的方法,如 c 中的 random()函数,Matlab中的rand()函数等。但这些生成器生成的随机数效果很不一样,比如 c 中的函数生成的随机数性质就比较差,如果用 c,最好自己再编一个程序。Matlab 中的 rand()函数,经过了很多优化。可以产生性质很好的随机数,可以直接利用。15 下面用计算机产生的伪随机数来模拟抛硬币实下面用计算机产生的伪随机数来模拟抛硬币实验。假设抛验。假设抛10次硬币构成一个事件。调用次硬币构成一个事件。调用Random(2)返回一个二值结果。返回返回一个二值结果。返回0表示抛硬币表示抛硬币得到反面,返回得到反面,返回1表示得到正面。下面的算法表示得到正面。下面的算法TossCoins模拟抛模拟抛10次硬币这一事件次硬币这一事件50000次。用次。用headi(0 i 10)记录这记录这50000此模拟恰好得到此模拟恰好得到i次次正面的次数。最终输出模拟抛硬币事件得到正面事正面的次数。最终输出模拟抛硬币事件得到正面事件的频率图,如下图所示:件的频率图,如下图所示:16 0 *1 *2 *3 *4 *5 *6 *7 *8 *9 *10 *模拟抛硬币得到的正面事件频率图模拟抛硬币得到的正面事件频率图17void main(void)/模拟随机抛硬币事件模拟随机抛硬币事件const int NCOINS=10;const long NTOSSES=50000L;/headsi是得到是得到i次正面的次数次正面的次数long i,headsNCOINS+1;int j,position;/初始化数组初始化数组headsfor(j=0;j NCOIN+1;j+)headsj=0;/重复重复50000次模拟事件次模拟事件for(i=0;i NTOSSES;i+)headsTossCoins(NCOINS)+;/输出频率图输出频率图for(i=0;i=NCOINS;i+)position=int(float(headsi)/NTOSSES*72);coutsetw(6)i;for(j=0;jposition-1;j+)cout;cout*endl;int TossCoins(int numberCoins)/随机抛硬币随机抛硬币 static RandomNumber coinToss;int i,tosses=0;for(i=0;i numberCoins;i+)/Random(2)=1 表示正面表示正面 tosses+=coinToss.Random(2);return tosses;int TossCoins(int numberCoins)/随机抛硬币随机抛硬币 static RandomNumber coinToss;int i,tosses=0;for(i=0;i numberCoins;i+)/Random(2)=1 表示正面表示正面 tosses+=coinToss.Random(2);return tosses;187.2 数值随机化算法数值随机化算法197.2 数值随机化算法数值随机化算法7.2.1 用随机投点法计算用随机投点法计算 值值值值 设有一半径为设有一半径为r的圆及其外切四边形。向该正的圆及其外切四边形。向该正方形随机地投掷方形随机地投掷n个点。设落入圆内的点数为个点。设落入圆内的点数为k。由于所投入的点在正方形上均匀分布,因而所投由于所投入的点在正方形上均匀分布,因而所投入的点落入圆内的概率为入的点落入圆内的概率为 。所以当所以当n足够大时,足够大时,k与与n之比就逼近这一概率。从之比就逼近这一概率。从而而 。20在具体实现时,只要在第一象限计算即可:在具体实现时,只要在第一象限计算即可:double Darts(int n)/用随机投点法计算用随机投点法计算 值值 static RandomNumber dart;int k=0;for(int i=1;i=n;i+)double x=dart.fRandom();double y=dart.fRandom();if(x*x+y*y)=1)k+;return 4*k/double(n);217.2.2 计算定积分计算定积分(1)用随机投点法计算定积分)用随机投点法计算定积分 设设f(x)是是0,1上的连续函数,且上的连续函数,且0 f(x)1。需要计算的积。需要计算的积分分为为 ,积分积分 I 等于图中的面积等于图中的面积G。在图所示单位正方形内均匀地作投点试在图所示单位正方形内均匀地作投点试验,则随机点落在曲线下面的概率为:验,则随机点落在曲线下面的概率为:假设向单位正方形内随机地投入假设向单位正方形内随机地投入n个个点点(xi,yi),i=1,2,n。随机点(。随机点(xi,yi)落入)落入G内,则内,则yi f(xi)。)。如果有如果有m个点落入个点落入G内,则随机点落入内,则随机点落入G内的概率,即内的概率,即22double Darts(int n)/用随机投点法计算定积分用随机投点法计算定积分static RandomNumber dart;int k=0;for(int i=1;i=n;i+)double x=dart.fRandom();double y=dart.fRandom();if(y=f(x)k+;return k/double(n)237.3 舍伍德算法舍伍德算法247.3 舍伍德算法舍伍德算法 设设A是一个确定性算法,当它的输入实例为是一个确定性算法,当它的输入实例为x时所需的时所需的计算时间记为计算时间记为tA(x)。设。设Xn是算法是算法A的输入规模为的输入规模为n的实例的实例的全体,则当问题的输入规模为的全体,则当问题的输入规模为n时,算法时,算法A所需的平均时所需的平均时间为间为 这显然不能排除存在这显然不能排除存在xXn使得使得 的可的可能性。希望获得一个随机化算法能性。希望获得一个随机化算法B,使得对问题的输入规,使得对问题的输入规模为模为n的每一个实例均有的每一个实例均有 这就是舍伍德算法设计的基本思想。当这就是舍伍德算法设计的基本思想。当s(n)与与 相比可忽略时,舍伍德算法可获得很好的平均性能。相比可忽略时,舍伍德算法可获得很好的平均性能。257.3.1 随机快速算法随机快速算法7.3.2 随机选择算法随机选择算法7.3.2 搜索有序表搜索有序表267.3.1 随机快速排序算法随机快速排序算法随机快速排序算法随机快速排序算法快速排序算法快速排序算法算法的核心在于选择合适的划分基准算法的核心在于选择合适的划分基准改变快速排序算法性能不稳定,即输入相关的问题改变快速排序算法性能不稳定,即输入相关的问题27template void quicksort_random(Type A,int low,int high)random_seed(0);/*选择系统当前时间作为随机数种子选择系统当前时间作为随机数种子*/r_quicksort(A,low,high);/*递归调用随机快速排序算法递归调用随机快速排序算法*/void r_quicksort(Type A,int low,int high)int k;if(lowhigh)k=random(low,high);/*产生产生low到到high之间的随机之间的随机数数k*/swap(Alow,Ak);/*把元素把元素Ak交换到数组交换到数组的第一个位置的第一个位置*/k=split(A,low,high);/*按元素按元素Alow把数组划分为把数组划分为两个两个*/r_quicksort(A,low,k-1);/*排序第一个子数组排序第一个子数组*/r_quicksort(A,k+1,high);/*排序第二个子数组排序第二个子数组*/随机快速排序算法随机快速排序算法28最坏时间复杂度仍是:最坏时间复杂度仍是:O(n2)最坏情况:当随机数发生器第最坏情况:当随机数发生器第i次随机产生的枢点次随机产生的枢点元素恰恰就是数组中第元素恰恰就是数组中第i大或第大或第i小的元素时造成最小的元素时造成最坏情况坏情况与输入无关与输入无关情况是微乎其微的情况是微乎其微的输入元素的任何排列顺序,都不可能使算法行为输入元素的任何排列顺序,都不可能使算法行为处于最坏的情况处于最坏的情况期望运行时间是期望运行时间是O(nlogn)297.3.2 随机选择算法随机选择算法选择问题选择问题给定线性序集中给定线性序集中n个元素和一个整数个元素和一个整数1kn,要求找出,要求找出这这n个元素中第个元素中第k小的元素小的元素 对选择问题而言,用中位数作为划分基准可以保证在对选择问题而言,用中位数作为划分基准可以保证在最坏情况下用线性时间完成选择。如果只简单地用待划分最坏情况下用线性时间完成选择。如果只简单地用待划分数组的第一个元素作为划分基准,则算法的平均性能较好,数组的第一个元素作为划分基准,则算法的平均性能较好,而在最坏的情况下需要而在最坏的情况下需要O(n2)计算时间。计算时间。舍伍德型选择算法则随机地一个数组元素作为划分基舍伍德型选择算法则随机地一个数组元素作为划分基准。这样既能保证算法的线性时间平均性能又避免了计算准。这样既能保证算法的线性时间平均性能又避免了计算拟中位数的麻烦。拟中位数的麻烦。30templateType select(Type a,int l,int k)/计算计算al:r中第中第k小元素小元素 static RandomNumber rnd;while(true)if(l=r)return al;int i=l,j=l+rnd.Random(r-l+1);/随机选择划分基准随机选择划分基准 swap(ai,aj);j=r+1;Type pivot=al;/以划分基准为轴作元素交换以划分基准为轴作元素交换 while(true)while(a+ipivot);if(i=j)break;Swap(ai,aj);if(j-l+1=k)return pivot;al=aj;aj=pivot;/对子数组重复划分过对子数组重复划分过程程if(j-l+1k)k=k-j+l-1;l=j+1;else r=j-1;31 由于算法由于算法select使用随机数产生器随机地产生使用随机数产生器随机地产生1和和r之间的随之间的随机整数。因此,算法机整数。因此,算法select所产生的划分基准是随机的。在这所产生的划分基准是随机的。在这个条件下,可以证明,当用算法个条件下,可以证明,当用算法select对含有对含有n个元素的数组进个元素的数组进行划分时,划分出的低区子数组中含有行划分时,划分出的低区子数组中含有1个元素的概率为个元素的概率为2/n;含有含有i个元素的概率为个元素的概率为1/n,i=2,3,n-1。设。设T(n)是算法是算法select作作用于一个含有用于一个含有n个元素的输入数组上所需的期望时间的一个上个元素的输入数组上所需的期望时间的一个上界,且界,且T(n)是单调递增的。在最坏情况下,第是单调递增的。在最坏情况下,第k小元素总是被小元素总是被划分在较大的子数组中。由此,可以得到关于划分在较大的子数组中。由此,可以得到关于T(n)的递归式:的递归式:32在上面的推导中,从第一行到第二行是因为在上面的推导中,从第一行到第二行是因为max(1,n-1)=n-1并且当并且当n是是奇奇数时,数时,T(n/2),T(n/2+1),T(n-1)在和式中均出现在和式中均出现2次;次;n 是偶数时,是偶数时,T(n/2+1),T(n/2+2),T(n-1)均出现两次,均出现两次,T(n/2)只出现一次。因此,第二行中的和式是第一行中和式的一只出现一次。因此,第二行中的和式是第一行中和式的一个上界。从第个上界。从第2行到第行到第3行是因为在最坏情况下行是因为在最坏情况下T(n-1)=O(n2),故故可将可将T(n-1)/n包含在包含在O(n)中。由此可知,非递归的舍伍德型中。由此可知,非递归的舍伍德型选择算法可以在选择算法可以在O(n)平均时间内实现。平均时间内实现。33 上述舍伍德选择算法对确定性选择算法所做的修改非常上述舍伍德选择算法对确定性选择算法所做的修改非常简单且容易实现。但有时所给的确定性算法无法直接改造成简单且容易实现。但有时所给的确定性算法无法直接改造成舍伍德算法。此时可借助随机预处理技术,不改变原有的确舍伍德算法。此时可借助随机预处理技术,不改变原有的确定性算法仅对其输入进行定性算法仅对其输入进行随机洗牌随机洗牌,同样可收到舍伍德算法,同样可收到舍伍德算法的效果。例如,对于确定性选择算法,可以用下面的洗牌算的效果。例如,对于确定性选择算法,可以用下面的洗牌算法法Shuffle将数组将数组a中的元素随机排列,然后用确定性选择算中的元素随机排列,然后用确定性选择算法求解。这样做的效果与舍伍德型算法是一样的。法求解。这样做的效果与舍伍德型算法是一样的。template void Shuffle(Type a,int n)/随机洗牌算法随机洗牌算法static RandomNumber rnd;for(int i=0;i0。设t(x)是算法obstinate找到具体实例x的一个解所需的平均时间,s(x)和e(x)分别是算法对于具体实例x求解成功或求解失败所需的平均时间,则有:解此方程可得:35n后问题后问题对于对于n后问题的任何一个解而言,每一个皇后在棋盘上的位置无后问题的任何一个解而言,每一个皇后在棋盘上的位置无任何规律,不具有系统性,而更象是随机放置的。由此容易想到任何规律,不具有系统性,而更象是随机放置的。由此容易想到下面的下面的拉斯维加斯算法拉斯维加斯算法。在棋盘上相继的各行中随机地放置皇后,并注意使新放置的皇后在棋盘上相继的各行中随机地放置皇后,并注意使新放置的皇后与已放置的皇后互不攻击,直至与已放置的皇后互不攻击,直至n个皇后均已相容地放置好,或个皇后均已相容地放置好,或已没有下一个皇后的可放置位置时为止。已没有下一个皇后的可放置位置时为止。如果将上述随机放置策略与回溯法相结合,可能会获得更好的如果将上述随机放置策略与回溯法相结合,可能会获得更好的效果。可以先在棋盘的若干行中随机地放置皇后,然后在后继效果。可以先在棋盘的若干行中随机地放置皇后,然后在后继行中用回溯法继续放置,直至找到一个解或宣告失败。随机放行中用回溯法继续放置,直至找到一个解或宣告失败。随机放置的皇后越多,后继回溯搜索所需的时间就越少,但失败的概置的皇后越多,后继回溯搜索所需的时间就越少,但失败的概率也就越大。率也就越大。36n 皇后问题皇后问题问题描述n皇后问题要求将n个皇后放在nn棋盘的不同行、不同列、不同斜线的位置,找出相应的放置方案 随机化措施对某行放置皇后的有效位置进行随机对某行所有列位置进行随机37n 皇后问题皇后问题Class Queen public:friend void nQueen(int);private:bool Place(int k);/测试皇后k置于第xk列的合法性 bool QueenLV(void);/随机放置n个皇后拉斯维加斯算法 int n,*x,*y;/n表示皇后个数,x和y表示解向量;Bool Queen:Place(int k)for(int j=1;jk;j+)if(abs(k-j)=abs(x j-xk)|(x j=xk)return false;return true;38对某行放置皇后的有效位置进行随机算法对某行放置皇后的有效位置进行随机算法bool Queen:QueensLV()RandomNumber rnd;/随机数产生器int k=1;/下一个放置的皇后编号int count=1;/记录当前要放置的第k个皇后在第k行的有效位置数while(k0)count=0;for(int i=1;i0)xk+=yrnd.Random(count);return(count0);/count0表示放置成功39对某行所有列位置进行随机的拉斯维加斯算法对某行所有列位置进行随机的拉斯维加斯算法bool Queen:QueensLV1(void)/棋盘上随机放置n个皇后拉斯维加斯算法RandomNumber rnd;/随机数产生器int k=1;/下一个放置的皇后编号int count=maxcout;/尝试产生随机位置的最大次数,用户根据需要设置while(k=n)int i=0;for(i=1;i=count;i+)xk=rnd.Random(n)+1;if(Place(k)break;/第k个皇后在第k行的有效位置存于y数组 if(in);/kn表示放置成功40整数因子分解整数因子分解设n1是一个整数。关于整数n的因子分解问题是找出n的如下形式的惟一分解式:其中,p1p2pk是k个素数,m1,m2,mk是k个正整数。如果n是一个合数,则n必有一个非平凡因子x,1xn,使得x可以整除n。给定一个合数n,求n的一个非平凡因子的问题称为整数n的因子分割问题。private static int split(int n)int m=(int)Math.floor(Math.sqrt(double)n);for(int i=2;i=m;i+)if(n%i=0)return i;return 1;事实上,算法split(n)是对范围在1x的所有整数进行了试除而得到范围在1x2的任一整数的因子分割。41Pollard算法算法在开始时选取0n-1范围内的随机数,然后递归地由产生无穷序列对于i=2k,以及2k1)&(dn)System.out.println(d);if(i=k)y=x;k*=2;对Pollard算法更深入的分析可知,执行算法的while循环约 次后,Pollard算法会输出n的一个因子p。由于n的最小素因子p ,故Pollard算法可在O(n1/4)时间内找到n的一个素因子。42蒙特卡罗蒙特卡罗(Monte Carlo)算法算法在实际应用中常会遇到一些问题,不论采用确定性算法或概率算法都无法保证每次都能得到正确的解答。蒙特卡罗算法则在一般情况下可以保证对问题的所有实例都以高概率给出正确解,但是通常无法判定一个具体解是否正确。设p是一个实数,且1/2pn/2时,称元素x是数组T的主元素。public static boolean majority(intt,int n)/判定主元素的蒙特卡罗算法 rnd=new Random();int i=rnd.random(n)+1;int x=ti;/随机选择数组元素 int k=0;for(int j=1;jn/2);/kn/2 时t含有主元素 public static boolean majorityMC(intt,int n,double e)/重复 次调用算法majority int k=(int)Math.ceil(Math.log(1/e)/Math.log(2);for(int i=1;i0,算法majorityMC重复调用log(1/)次算法majority。它是一个偏真蒙特卡罗算法,且其错误概率小于。算法majorityMC所需的计算时间显然是O(nlog(1/)。45素数测试素数测试WilsonWilson定理定理定理定理:对于给定的正整数n,判定n是一个素数的充要条件是(n-1)!-1(mod n)。费尔马小定理费尔马小定理费尔马小定理费尔马小定理:如果p是一个素数,且0ap,则ap-1(mod p)。二次探测定理二次探测定理二次探测定理二次探测定理:如果p是一个素数,且0 xp,则方程x21(mod p)的解为x=1,p-1。private static int power(int a,int p,int n)/计算 ap mod n,并实施对n的二次探测 int x,result;if(p=0)result=1;else x=power(a,p/2,n);/递归计算 result=(x*x)%n;/二次探测 if(result=1)&(x!=1)&(x!=n-1)composite=true;if(p%2)=1)/p是奇数 result=(result*a)%n;return result;public static boolean prime(int n)/素数测试的蒙特卡罗算法 rnd=new Random();int a,result;composite=false;a=rnd.random(n-3)+2;result=power(a,n-1,n);if(composite|(result!=1)return false;else return true;算法prime是一个偏假3/4正确的蒙特卡罗算法。通过多次重复调用错误概率不超过(1/4)k。这是一个很保守的估计,实际使用的效果要好得多。46POJ例题分析例题分析POJ 3318给出给出3个个n*n(n500)的矩阵)的矩阵A,B,C,验证,验证A*B=C?n3算法会超时。n2的概率算法:随机置换法则:如果比特向量a b,r为随机向量,那么 Pr(a,r)(b,r)1/2随机一个1*n的向量r,验证A*B*r=C*r,若成立,输出“YES”,否则输出“NO”。如果A*BC,算法以1/2的概率输出“NO”!47POJ例题分析例题分析POJ2576有一堆数,需要分为两堆。要求两堆之间元素个数差不有一堆数,需要分为两堆。要求两堆之间元素个数差不超过超过1,并且两堆和的差值尽量小。(元素个数,并且两堆和的差值尽量小。(元素个数100,元素元素值值 450)动态规划可以解决。随机算法如下:1.计算两堆的大小后,随机分为两堆。2.随机选择两堆中的数,如果交换后差变小,则交换。3.重复2,直到多次交换未发生。更新答案。4.回到1,重新开始。48