隨機化算法


隨機化算法特征:

對於所求問題的同一實例用同一隨機化算法求解兩次可能得到完全不同的結果,這兩次求解的時間甚至得到的結果可能會有相當大的差別。

 

分類:

1.數值隨機化算法

這類算法常用於數值問題的求解,所得到的解往往都是近似解,而且近似解的精度隨計算時間的增加不斷提高。

使用該算法的理由是:在許多情況下,待求解的問題在原理上可能就不存在精確解,或者說精確解存在但無法在可行時間內求得,因此用數值隨機化算法可以得到相當滿意的解。

 

示例:計算圓周率Π的值

將n個點隨機投向一個正方形,設落人此正方形內切圓(半徑為r)中的點的數目為k。

假設所投入的點落入正方形的任一點的概率相等,則所投入的點落入圓內的概率為Π*r2/4*r2=Π/4。當n→∞時,k/n→Π/4,從而Π約等於4*k/n。

 

#include <bits/stdc++.h>
using namespace std; typedef long long ll; const int maxn = 1e2 + 10; const int INF = 0x3f3f3f3f; #define m 65536L
#define b 1194211693L
#define c 12345L
class RandomNumber { private: unsigned long d;    //d為當前種子
    public: RandomNumber(unsigned long s=0);    //默認值0表示由系統自動給種子
        unsigned short random(unsigned long n); //產生0:n-1之間的隨機整數
        double fRandom(void); //產生[0,1)之間的隨機實數
}; //函數RandomNumber用來產生隨機數種子
RandomNumber::RandomNumber(unsigned long s) { if(s==0) d=time(0); //由系統提供隨機種子
    else d=s;   //由用戶提供隨機種子
} unsigned short RandomNumber::random(unsigned long n) { d=b*d+c;  //用線性同余式計算新的種子d
    return (unsigned short)((d>>16)%n); //把d的高16位映射到0~(n-1)范圍內
} double RandomNumber::fRandom(void) { return random(m)/double(m); } double Darts(int n) { static RandomNumber darts; int k=0,i; double x,y; for(int i=1;i<=n;++i) { x=darts.fRandom(); y=darts.fRandom(); if((x*x+y*y)<=1) k++; } return 4.0*k/double(n); } int main() { srand(time(NULL)); //指定運行次數,次數越高精度越高
    printf("%.8lf\n",Darts(10000)); return 0; }

 

 

2.蒙特卡羅算法
家特卡羅算法是計算數學中的一 種計算方法,它的基本特點是以概率與統計學中的理論和方法為基礎,以是否適合於在計算機上使用為重要標志。蒙特卡羅是摩納哥的一個著名城市,以賭博聞名於世。為了表明該算法的上述基本特點,蒙特卡羅算法象征性地借用這一.城市的名稱來命名。蒙特卡羅算法作為一種可行的計算方法,首先是由Ulam(烏拉姆)和、Neumann(馮 .諾依曼)在 20世紀40年代中葉提出並加以運用,目的是為了解決研制核武器中的計算問題。
該算法用於求問題的准確解。對於許多問題來說,近似解毫無意義。例如,一個判定問題其解為“是”或“否”,二者必居其一,不存在任何近似解答。
蒙特卡羅算法的特點是:它能求得問題的一個解,但這個解未必是正確的。 求得正確解的概率依賴於算法執行時所用的時間,所用的時間越多得到正確解的概率就越高。一般情況下,蒙特卡羅算法不能有效地確定求得的解是否正確。
 
示例:主元素問題
設T[1:n]是一個含有n個元素的數組,當|{i|T[i]等於x}|>n/2時,稱元素x是數組T的主元素/對於給定的含有n個元素的數組T,設定確定數組T中是否存在主元素的程序
int majority(int T[],int n) { RandomNumber rnd; int i=rnd.random(n)+1; int x=T[i]; //printf("%d %d\n",i,x);
    int k=0; for(int j=1;j<=n;++j) { if(T[j]==x)k++; } if(k>n/2) { return x; } else return -INF; }
解析:
蒙特卡洛算法對於問題的任一實例得到正確解的概率不低於p(0.5<=p<=1),要想提高p,需要增加majority程序運行次數。我們設得到正確解概率不能低於1-ci(0<ci<=1-p),假設需要調用x次,則p+(1-p)*p+(1-p)2*p+...+(1-p)x-1*p>=1-ci,即1-(1-p)>=1-ci,因為0<1-p<1/2,所以x>=log1-pci,故x=ceil(log2ci/log2(1-p)).
總的來說,ci越接近0越好
#include <bits/stdc++.h>
using namespace std; typedef long long ll; const int maxn = 1e2 + 10; const int INF = 0x3f3f3f3f; #define m 65536L
#define b 1194211693L
#define c 12345L
class RandomNumber { private: unsigned long d;    //d為當前種子
    public: RandomNumber(unsigned long s=0);    //默認值0表示由系統自動給種子
        unsigned short random(unsigned long n); //產生0:n-1之間的隨機整數
        double fRandom(void); //產生[0,1)之間的隨機實數
}; //函數RandomNumber用來產生隨機數種子
RandomNumber::RandomNumber(unsigned long s) { if(s==0) d=time(0); //由系統提供隨機種子
    else d=s;   //由用戶提供隨機種子
} unsigned short RandomNumber::random(unsigned long n) { d=b*d+c;  //用線性同余式計算新的種子d
    return (unsigned short)((d>>16)%n); //把d的高16位映射到0~(n-1)范圍內
} double RandomNumber::fRandom(void) { return random(m)/double(m); } int T[maxn]; int majority(int T[],int n) { RandomNumber rnd; int i=rnd.random(n)+1; int x=T[i]; //printf("%d %d\n",i,x);
    int k=0; for(int j=1;j<=n;++j) { if(T[j]==x)k++; } if(k>n/2) { return x; } else return -INF; } int majorityMC(int T[],int n,double ci) { int k=(int)ceil(log(ci)/log(0.5)); printf("%d\n",k); for(int i=1;i<=k;++i) { int tmp=majority(T,n); if(tmp!=-INF) return tmp; } return -INF; } int main() { int n; scanf("%d",&n); for(int i=1;i<=n;++i) { scanf("%d",&T[i]); } int tmp=majorityMC(T,n,0.09); printf("%d\n",tmp); return 0; }

 

3.拉斯維加斯算法
該算法絕不返回錯誤的解,也就是說,使用拉斯維加斯算法不會得到不正確的解,-旦找到一個解,那么這個解肯定是正確的。但是有時候拉斯維加斯算法可能找不到解。
與蒙特卡羅算法類似,拉斯維加斯算法得到正確解的概率隨着算法執行時間的增加而提高。對於所求解問題的任一實例,只要用同一 -拉斯維加斯算法對該實例反復求解足夠多的次數,可使求解失效的概率任意小。
 
給一個分解給定整數因子的程序

 

偷個懶(嘻嘻 

 
4.舍伍德算法
當一個確定性算法在最壞情況下的計算時間復雜性與其在平均情況下的計算復雜性有較大差異時,可在這個確定性算法中引/人隨機性來降低樂最壞情況出現的概率,進面消除或減少問題好壞實例之間的這種差異,這樣的隨機化算法稱為舍伍德算法,因此,含伍德算法不會改變對應確定性算法的求解結果,;每次運行於都能夠得到問題的解,並且所得到的解是正確情況 的發生,而是降低最壞情況發生的概率。故而,舍伍德算法不改變原有算法的平均性能,只是設法保證以更高概率獲得算法的平均計算性能。
這個算法的實例也就是快速排序中加入了隨機選取標准值增快了速度
  

 -------------------------------------------------------------------------------------------------------------

一、隨機排列數組

隨機排列要求對於數組中每一個元素出現到任意一個位置的概率為1/n(數組長度為n)

偽代碼:

RANDOMIZE-IN-PLACE ( A , n ) for i ←1 to n do swap A[i] ↔A[RANDOM(i , n )]

證明:

A[1]位於位置1的概率為1/n,這個顯然,因為A[1]不被1到n的元素替換的概率為1/n,而后就不會再改變A[i]了。而A[1]位於位置2的概率也是1/n,因為A[1]要想位於位置2,則必須在第一次與A[k]交換(k=2...n),同時第二次A[2]與A[k]替換,第一次與A[k]交換的概率為(n-1)/n,而第二次替換概率為1/(n-1),所以總的概率是(n-1)/n * 1/(n-1) = 1/n。同理可以推導其他情況。

當然這個條件只能是隨機排列數組的一個必要條件,也就是說,滿足元素A[i]位於位置j的概率為1/n不一定就能說明這可以產生隨機排列數組。因為它可能產生的排列數目少於n!,盡管概率相等,但是排列數目沒有達到要求。

但是上面這個算法RANDOMIZE-IN-PLACE可以產生均勻隨機排列,證明過程見:https://blog.csdn.net/sgbfblog/article/details/7917685

 

二、隨機選取給定整數流的一個數字

整數流也就是由數字構成的連續序列,這個也要保證整數流每一個位置的數字取到的概率相等

解:

如果數據流長度為1,那么必選第1個數字。

如果數據流長度為2,那么我們選第2個數字的概率為1/2,我們以1/2的概率用第2個數字替換前面選的隨機數,得到新的隨機數。

.........

如果數據流長度為n,那么我們選擇第n個數字的概率為1/n,即我們以1/n的概率用第n個數字替換前面選的隨機數,得到新的隨機數。

一個簡單的方法就是使用隨機函數f(n)=bigrand()%n,其中bigrand()返回很大的隨機整數,當數據流到第n個數時,如果f(n)==0,則替換前面的已經選的隨機數,這樣可以保證每個數字被選中的概率都是1/n。如當n=1時,則f(1)=0,則選擇第1個數,當n=2時,則第2個數被選中的概率為1/2,以此類推,當數字長度為n時,第n個數字被選中的概率為1/n。

 

三、隨機從n個數里面選取m個數

代碼:

void genknuth(int m, int n) { for (int i=0; i<n; i++) //必定會選取m個數,當n-i==m的時候,那么對於區間[i,n]每一個數都會被選取 if (bigrand() % (n-i) < m) {  //n-i中i每次加1,相當於remaining每次減1
                 cout << i << endl; m--;  //選取的數目減1
 } }

先考慮個簡單的例子,當m=2,n=5時,我們需要從0~4這5個整數中等概率的選取2個有序的整數,且不能重復。如果采用如下條件選取:bigrand() % 5 < 2,則我們選取0的概率為2/5。但是我們不能采取同樣的概率來選取1,因為選取了0后,我們應該以1/4的概率來選取1,而在沒有選取0的情況下,我們應該以2/4的概率選取1。

-------------------------------------------------------------------------------------------------------------

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM