概率算法簡介 很多算法的每一個計算步驟都是固定的,而在下面我們要討論的概率算法,允許算法在執(zhí)行的過程中隨機(jī)選擇下一個計算步驟。許多情況下,當(dāng)算法在執(zhí)行過程中面臨一個選擇時,隨機(jī)性選擇常比最優(yōu)選擇省時。因此概率算法可在很大程度上降低算法的復(fù)雜度。 概率算法的一個基本特征是對所求解問題的同一實(shí)例用同一概率算法求解兩次可能得到完全不同的效果。這兩次求解問題所需的時間甚至所得到的結(jié)果可能會有相當(dāng)大的差別。一般情況下,可將概率算法大致分為四類:數(shù)值概率算法,蒙特卡羅(Monte Carlo)算法,拉斯維加斯(Las Vegas)算法和舍伍德(Sherwood)算法。 數(shù)值概率算法常用于數(shù)值問題的求解。這類算法所得到的往往是近似解。而且近似解的精度隨計算時間的增加不斷提高。在許多情況下,要計算出問題的精確解是不可能或沒有必要的,因此用數(shù)值概率算法可得到相當(dāng)滿意的解。 蒙特卡羅算法用于求問題的準(zhǔn)確解。對于許多問題來說,近似解毫無意義。例如,一個判定問題其解為“是”或“否”,二者必居其一,不存在任何近似解答。又如,我們要求一個整數(shù)的因子時所給出的解答必須是準(zhǔn)確的,一個整數(shù)的近似因子沒有任何意義。用蒙特卡羅算法能求得問題的一個解,但這個解未必是正確的。求得正確解的概率依賴于算法所用的時間。算法所用的時間越多,得到正確解的概率就越高。蒙特卡羅算法的主要缺點(diǎn)就在于此。一般情況下,無法有效判斷得到的解是否肯定正確。 拉斯維加斯算法不會得到不正確的解,一旦用拉斯維加斯算法找到一個解,那么這個解肯定是正確的。但是有時候用拉斯維加斯算法可能找不到解。與蒙特卡羅算法類似。拉斯維加斯算法得到正確解的概率隨著它用的計算時間的增加而提高。對于所求解問題的任一實(shí)例,用同一拉斯維加斯算法反復(fù)對該實(shí)例求解足夠多次,可使求解失效的概率任意小。 舍伍德算法總能求得問題的一個解,且所求得的解總是正確的。當(dāng)一個確定性算法在最壞情況下的計算復(fù)雜性與其在平均情況下的計算復(fù)雜性有較大差別時,可以在這個確定算法中引入隨機(jī)性將它改造成一個舍伍德算法,消除或減少問題的好壞實(shí)例間的這種差別。舍伍德算法精髓不是避免算法的最壞情況行為,而是設(shè)法消除這種最壞行為與特定實(shí)例之間的關(guān)聯(lián)性。 本文簡要的介紹一下數(shù)值概率算法和舍伍德算法。 首先來談?wù)勲S機(jī)數(shù)。隨機(jī)數(shù)在概率算法設(shè)計中扮演著十分重要的角色。在現(xiàn)實(shí)計算機(jī)上無法產(chǎn)生真正的隨機(jī)數(shù),因此在概率算法中使用的隨機(jī)數(shù)都是一定程度上隨機(jī)的,即偽隨機(jī)數(shù)。 產(chǎn)生隨機(jī)數(shù)最常用的方法是線性同余法。由線性同余法產(chǎn)生的隨機(jī)序列a1,a2,...,an滿足 a0=d an=(ban-1+c)mod m n=1,2....... 其中,b>=0, c>=0, d>=m。d稱為該隨機(jī)序列的種子。 下面我們建立一個隨機(jī)數(shù)類RadomNumber,該類包含一個由用戶初始化的種子randSeed。給定種子之后,既可產(chǎn)生與之相應(yīng)的隨機(jī)數(shù)序列。randseed是一個無符號長整型數(shù),既可由用戶指定也可由系統(tǒng)時間自動產(chǎn)生。 const unsigned long maxshort=65536L; const unsigned long multiplier=1194211693L; const unsigned long adder=12345L; class RandomNumber { private: //當(dāng)前種子 unsigned long randseed; public: //構(gòu)造函數(shù),缺省值0表示由系統(tǒng)自動產(chǎn)生種子 RandomNumber(unsigned long s=0); //產(chǎn)生0-n-1之間的隨機(jī)整數(shù) unsigned short Random(unsigned long n); //產(chǎn)生[0,1)之間的隨機(jī)實(shí)數(shù) double fRandom(void); }; RandomNumber::RandomNumber(unsigned long s) { if(s==0) randseed=time(0); else randseed=s; } unsigned short RandomNumber::Random(unsigned long n) { randseed=multiplier*randseed+adder; return (unsigned short)((randseed>>16)%n); } double RandomNumber::fRandom(void) { return Random(maxshort)/double(maxshort); } 函數(shù)Random在每次計算時,用線性同余式計算新的種子。它的高16位的隨機(jī)性較好,將randseed右移16位得到一個0-65535之間的隨機(jī)整數(shù)然后再將此隨機(jī)整數(shù)映射到0-n-1范圍內(nèi)。 對于函數(shù)fRandom,先用Random(maxshort)產(chǎn)生一個0-(maxshort-1之間的整型隨機(jī)序列),將每個整型隨機(jī)數(shù)除以maxshort,就得到[0,1)區(qū)間中的隨機(jī)實(shí)數(shù)。 下面來看看數(shù)值概率算法的兩個例子: 1.用隨機(jī)投點(diǎn)法計算π 設(shè)有一半徑為r的圓及其外切四邊形,如圖所示。向該正方形隨機(jī)投擲n個點(diǎn)。設(shè)落入圓內(nèi)的點(diǎn)在正方形上均勻分布,因而所投入點(diǎn)落入圓內(nèi)的概率為πr^2/4r^2,所以當(dāng)n足夠大時,k與n之比就逼近這一概率,即π/4。由此可得使用隨機(jī)投點(diǎn)法計算π值的數(shù)值概率算法。具體實(shí)現(xiàn)時,只需要在第一次象限計算即可。 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); } 再簡單舉個舍伍德算法的例子。 我們在分析一個算法在平均情況下的計算復(fù)雜性時,通常假定算法的輸入數(shù)據(jù)服從某一特定的概率分布。例如,在輸入數(shù)據(jù)是均勻分布時,快速排序算法所需的平均時間是O(n logn)。但是如果其輸入已經(jīng)基本上排好序時,所用時間就大大增加了。此時,可采用舍伍德算法消除算法所需計算時間與輸入實(shí)例間的這種聯(lián)系。 在這里,我們用舍伍德型選擇算法隨機(jī)的選擇一個數(shù)組元素作為劃分標(biāo)準(zhǔn)。這樣既能保證算法的線性時間平均性能又避免了計算擬中位數(shù)的麻煩。非遞歸的舍伍德型算法可描述如下: template<class Type> Type select(Type a[], int l, int r, int k) { static RandomNumber rnd; while(true){ if(l>=r) return a[l]; int i=l, j=l=rnd.Random(r-l+1); Swap(a[i], a[j]); j=r+1; Type pivot=a[l]; while(true) { while(a[++i]<pivot); while(a[--j]>pivot); if(i>=j) break; Swap(a[i], a[j]); } if(j-l+1==k) return pivot; a[l]=a[j]; a[j]=pivot; if(j-l+1<k) { k=k-j+l-1; l=j+1; } else r=j-1; } } template <class Type> Type Select(Type a[], int n, int k) { if(k<1||k>n) throw OutOfBounds(); return select(a, 0, n-1, k); } 平時我們一般開始考慮的是一個有著很好平均性能的選擇算法,但在最壞情況下對某些實(shí)例算法效率較低。這時候我們用概率算法,將上述算法改造成一個舍伍德型算法,使得該算法對任何實(shí)例均有效。 不過在有些情況下,所給的確定性算法無法直接改造成舍伍德型算法。這時候就可以借助隨機(jī)預(yù)處理技術(shù),不改變原有的確定性算法,僅對其輸入進(jìn)行隨機(jī)洗牌,同樣可以得到舍伍德算法的效果。還是剛才的例子,換一種方法實(shí)現(xiàn): template<class Type> void Shuffle(Type a[], int n) { static RandomNumber rnd; for(int i=1;i<n;i++){ int j=rnd.Random(n-i)+i; Swap(a[i], a[j]); } } 在上文里,我們對概率算法中的數(shù)值概率算法以及舍伍德算法舉例作了簡要的介紹,希望能使大家對概率算法有一個初步的認(rèn)識,并且將這種思想運(yùn)用到自己平時的編程中。 ----------------------------------------------
|
|