并行计算多线程概率法蒙特卡洛法求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;
}

在下还很菜,还请诸位多多指教,一起进步。

原创文章 5 获赞 3 访问量 371

猜你喜欢

转载自blog.csdn.net/pige666/article/details/106070050