並行計算多線程概率法蒙特卡洛法求pi

這次我們來介紹一下求pi值的第二種方法,概率法,也叫蒙特卡洛方法。
這個程序的原理十分簡單,如下圖。

然後我們可以發現,其實我們想要的答案已經顯而易見了。如果是串行的程序,似乎也沒什麼好說的。隨機生成n個x座標y座標均小於1的點。然後統計具體有多少個點落在圓弧範圍內即可。
在這裏插入圖片描述
但聰明如我發現了事情不會這麼簡單
經大佬提醒,我發現這樣子實現並行程序的速度極慢,甚至是不像一個並行程序,因此我們來探究一下事情可能發生在哪裏。

沒錯,思來想去,也就只有rand函數可能出了一些問題,那麼讓我們來看看rand()函數的本質是什麼吧!

rand()函數產生隨機數,但是,其內部實現是用線性同餘法實現的,是僞隨機數,由於週期較長,因此在一定範圍內可以看成是隨機的。
rand()會返回一個範圍在0到RAND_MAX(至少是32767)之間的僞隨機數(整數)。
在調用rand()函數之前,可以使用srand()函數設置隨機數種子,如果沒有設置隨機數種子,rand()函數在調用時,自動設計隨機數種子爲1。隨機種子相同,每次產生的隨機數也會相同。
rand函數在產生隨機數前,需要系統提供的生成僞隨機數序列的種子,rand根據這個種子的值產生一系列隨機數。如果系統提供的種子沒有變化,每次調用rand函數生成的僞隨機數序列都是一樣的。

經過仔細的分析,我們發現了一個名詞,種子!
Q:什麼是種子呢
A:是SNIS,哦不,是IPZ ,哦不不不,其實是這個——seed。
因此,我們知道了,rand()函數其實是線程不安全的,會導致種子競爭,因此並行性並不好。
那麼我們要怎麼辦呢?
經過一番搜索,我們發現了函數——rand_r()。

/* This algorithm is mentioned in the ISO C standard, here extended
   for 32 bits.  */
int
rand_r (unsigned int *seed)
{
  unsigned int next = *seed;
  int result;

  next *= 1103515245;
  next += 12345;
  result = (unsigned int) (next / 65536) % 2048;

  next *= 1103515245;
  next += 12345;
  result <<= 10;
  result ^= (unsigned int) (next / 65536) % 1024;

  next *= 1103515245;
  next += 12345;
  result <<= 10;
  result ^= (unsigned int) (next / 65536) % 1024;

  *seed = next;

  return result;
}

這樣的一個函數代表什麼呢?
它代表我們可以編寫真正的並行程序了!
GOGOGOGO!

void *thread_function(void *arg){
    unsigned int seed = 123;
    int sample = N/T;
    int demo = 0;
    for(int i=0;i<sample;i++){
        double x = 1.0*rand_r(&seed)/RAND_MAX;
        double y = 1.0*rand_r(&seed)/RAND_MAX;
        if(x*x + y*y < 1.0){
            demo++;
        }
    }
    pthread_mutex_lock(&mut);
    pi = (pi+demo);
    pthread_mutex_unlock(&mut);
    return NULL;
}

這樣,我們就得到了一個good的線程函數,接下來我們來看看完整的程序吧!

#include <iostream>
#include <pthread.h>
#include <iomanip>
#include <ctime>
using namespace std;

double pi = 0.0;
int N,T;
pthread_mutex_t mut;

//線程入口函數
void *thread_function(void *arg){
    unsigned int seed = 123;
    int sample = N/T;
    int demo = 0;
    for(int i=0;i<sample;i++){
        double x = 1.0*rand_r(&seed)/RAND_MAX;
        double y = 1.0*rand_r(&seed)/RAND_MAX;
        if(x*x + y*y < 1.0){
            demo++;
        }
    }
    pthread_mutex_lock(&mut);
    pi = (pi + demo);
    pthread_mutex_unlock(&mut);
    return NULL;
}

int main(int argc, const char * argv[]) {
    cout<<"並行積分法求兀的值,輸入覆蓋點個數和線程數"<<endl;
    cin>>N>>T;
    pthread_t thread[T];
    int x[T];
    for(int i=0;i<T;i++){
        x[i] = i;
        pthread_create(&thread[i], NULL, thread_function, &x[i]);
    }
    for(int i=0;i<T;i++){
        pthread_join(thread[i], NULL);
    }
     pi = 4*pi/N;
     cout<<setprecision(8)<<pi<<endl; 
    return 0;
}

在下還很菜,還請諸位多多指教,一起進步。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章