1. 程式人生 > 實用技巧 >蒙特卡羅演算法之主元素問題

蒙特卡羅演算法之主元素問題

1、蒙特卡羅演算法

基本概述

蒙特卡羅(Monte Carlo)方法,又稱隨機抽樣或統計試驗方法。傳統的經驗方法由於不能逼近真實的物理過程,很難得到滿意的結果,而蒙特卡羅方法由於能夠真實地模擬實際物理過程,故解決問題與實際非常符合,可以得到很圓滿的結果

在實際應用中常會遇到一些問題,不論採用確定性演算法或概率演算法都無法保證每次都能得到正確的解答。蒙特卡羅演算法則在一般情況下可以保證對問題的所有例項都以高概率給出正確解,但是通常無法判定一個具體解是否正確。

設p是一個實數,且1/2<p<1。如果一個蒙特卡羅演算法對於問題的任一例項得到正確解的概率不小於p,則稱該蒙特卡羅演算法是p正確的,且稱p-1/2是該演算法的優勢。


如果對於同一例項,蒙特卡羅演算法不會給出2個不同的正確解答,則稱該蒙特卡羅演算法是一致的
有些蒙特卡羅演算法除了具有描述問題例項的輸入引數外,還具有描述錯誤解可接受概率的引數。這類演算法的計算時間複雜性通常由問題的例項規模以及錯誤解可接受概率的函式來描述。

對於一個一致的P正確蒙特卡羅演算法,要提高獲得正確率的概率,只要執行該演算法若干次,並選擇出現頻次最高的解即可。

原理思想

當所要求解的問題是某種事件出現的概率,或者是某個隨機變數的期望值時,它們可以通過某種“試驗”的方法,得到這種事件出現的頻率,或者這個隨機變數的平均值,並用它們作為問題的解。這就是蒙特卡羅方法的基本思想。蒙特卡羅方法通過抓住事物運動的幾何數量和幾何特徵,利用數學方法來加以模擬,即進行一種數字模擬實驗。它是以一個概率模型為基礎,按照這個模型所描繪的過程,通過模擬實驗的結果,作為問題的近似解。

主要步驟

蒙特卡羅解題歸結為三個主要步驟:構造或描述概率過程;實現從已知概率分佈抽樣;建立各種估計量。

1)構造或描述概率過程: 對於本身就具有隨機性質的問題,如粒子輸運問題,主要是正確描述和模擬這個概率過程,對於本來不是隨機性質的確定性問題,比如計算定積分,就必須事先構造一個人為的概率過程,它的某些參量正好是所要求問題的解。即要將不具有隨機性質的問題轉化為隨機性質的問題。

2)實現從已知概率分佈抽樣: 構造了概率模型以後,由於各種概率模型都可以看作是由各種各樣的概率分佈構成的,因此產生已知概率分佈的隨機變數(或隨機向量),就成為實現蒙特卡羅方法模擬實驗的基本手段,這也是蒙特卡羅方法被稱為隨機抽樣

的原因。最簡單、最基本、最重要的一個概率分佈是(0,1)上的均勻分佈(或稱矩形分佈)。隨機數就是具有這種均勻分佈的隨機變數。隨機數序列就是具有這種分佈的總體的一個簡單子樣,也就是一個具有這種分佈的相互獨立的隨機變數序列。產生隨機數的問題,就是從這個分佈的抽樣問題。在計算機上,可以用物理方法產生隨機數,但價格昂貴,不能重複,使用不便。另一種方法是用數學遞推公式產生。這樣產生的序列,與真正的隨機數序列不同,所以稱為偽隨機數,或偽隨機數序列。不過,經過多種統計檢驗表明,它與真正的隨機數,或隨機數序列具有相近的性質,因此可把它作為真正的隨機數來使用。由已知分佈隨機抽樣有各種方法,與從(0,1)上均勻分佈抽樣不同,這些方法都是藉助於隨機序列來實現的,也就是說,都是以產生隨機數為前提的。由此可見,隨機數是我們實現蒙特卡羅模擬的基本工具。 建立各種估計量: 一般說來,構造了概率模型並能從中抽樣後,即實現模擬實驗後,我們就要確定一個隨機變數,作為所要求的問題的解,我們稱它為無偏估計
3)建立各種估計量:相當於對模擬實驗的結果進行考察和登記,從中得到問題的解。

2、主元素問題

問題描述

設T[1:n]是一個含有n個元素的陣列。當|{i|T[i]=x}|>n/2時,稱元素x是陣列T的主元素。例如:陣列T[]={5,5,5,5,5,5,1,3,4,6}中,元素T[0:5]為陣列T[]的主元素。

問題求解

演算法隨機選擇陣列元素x,由於陣列T的非主元素個數小於n/2,所以,x不為主元素的概率小於1/2。因此判定陣列T的主元素存在性的演算法是一個偏真1/2正確的演算法。50%的錯誤概率是不可容忍的,利用重複呼叫技術將錯誤概率降低到任何可接受的範圍內。對於任何給定的>0,演算法majorityMC重複呼叫次演算法majority。它是一個偏真蒙特卡羅演算法,且其錯誤概率小於。演算法majorityMC所需的計算時間顯然是。

程式碼實現:

//隨機化演算法 蒙特卡羅演算法 主元素問題
//#include "stdafx.h"
#include "RandomNumber.h"
#include <cmath>
#include <iostream>
using namespace std;
 
//判定主元素的蒙特卡羅演算法
template<class Type>
bool Majority(Type *T,int n)
{
    RandomNumber rnd;
    int i = rnd.Random(n);
 
    Type x = T[i];    //隨機選擇陣列元素
    int k = 0;
 
    for(int j=0; j<n; j++)
    {
        if(T[j] == x)
        {
            k++;
        }
    }
 
    return (k>n/2);    //k>n/2時,T含有主元素
}
 
//重複k次呼叫演算法Majority
template<class Type>
bool MajorityMC(Type *T,int n,double e)
{
    int k = ceil(log(1/e)/log((float)2));
    for(int i=1; i<=k; i++)
    {
        if(Majority(T,n))
        {
            return true;
        }
    }
    return false;
}
 
int main()
{
    int n = 10;
    float e = 0.001;
    int a[] = {5,5,5,5,5,5,1,3,4,6};
    cout<<"陣列a的元素如下:"<<endl;
    for(int i=0; i<10; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    cout<<"呼叫MajorityMC判斷陣列是否含有主元素結果是:"<<MajorityMC(a,n,e)<<endl;
}
View Code
#include"time.h"
//隨機數類
const unsigned long maxshort = 65536L;
const unsigned long multiplier = 1194211693L;
const unsigned long adder = 12345L;
 
class RandomNumber
{
    private:
        //當前種子
        unsigned long randSeed;
    public:
        RandomNumber(unsigned long s = 0);//建構函式,預設值0表示由系統自動產生種子
        unsigned short Random(unsigned long n);//產生0:n-1之間的隨機整數
        double fRandom(void);//產生[0,1)之間的隨機實數
};
 
RandomNumber::RandomNumber(unsigned long s)//產生種子
{
    if(s == 0)
    {
        randSeed = time(0);//用系統時間產生種子
    }
    else
    {
        randSeed = s;//由使用者提供種子
    }
}
 
unsigned short RandomNumber::Random(unsigned long n)//產生0:n-1之間的隨機整數
{
    randSeed = multiplier * randSeed + adder;//線性同餘式
    return (unsigned short)((randSeed>>16)%n);
}
 
double RandomNumber::fRandom(void)//產生[0,1)之間的隨機實數
{
    return Random(maxshort)/double(maxshort);
}
View Code

實現結果:

參考文獻:王曉東《演算法設計與分析》

https://blog.csdn.net/liufeng_king/article/details/9251589