使用 TRNG 随机生成

Random generation with TRNG

对于以下为 Monte Carlo 模拟生成随机数的代码,我需要接收每个 运行 的确切总和,但这不会发生,尽管我已经修复了种子。如果有人能指出此代码的问题,我将不胜感激

#include <cmath>
#include <random>
#include <iostream>
#include <chrono>
#include <cfloat>
#include <iomanip>
#include <cstdlib>
#include <omp.h>
#include <trng/yarn2.hpp>
#include <trng/mt19937_64.hpp>
#include <trng/uniform01_dist.hpp>

using namespace std;
using namespace chrono;
const double landa = 1; 
const double exact_solution = landa / (pow(landa, 2) + 1); 
double function(double x) {
    return cos(x) / landa;
}

int main() {
    int rank; 
    const int N = 1000000;
    double sum = 0.0;
    trng::yarn2 r[6];
    for (int i = 0; i <6; i++)
    { 
        r[i].seed(0); 
    }
    
    for (int i = 0; i < 6; i++)
    {
          r[i].split(6,i);
    }
    
     trng::uniform01_dist<double> u;
     auto start = high_resolution_clock::now();
     #pragma omp parallel  num_threads(6)  
     {
         rank=omp_get_thread_num();
         #pragma omp for reduction (+: sum)
         for (int i = 0; i<N; ++i) {
            //double x = distribution(g);
      
            double x= u(r[rank]); 
            x = (-1.0 / landa) * log(1.0 - x); 
            sum = sum+function(x);
         }
    }
     double app   = sum / static_cast<double> (N);
     auto end = high_resolution_clock::now();
        
    auto diff=duration_cast<milliseconds>(end-start);
    
    cout << "Approximation is: " <<setprecision(17) << app << "\t"<<"Time: "<< setprecision(17) << diff.count()<<" Error: "<<(app-exact_solution)<< endl; 

    return 0;
}

TL;DR 问题有两个:

  1. 浮点加法是非关联的;
  2. 您正在为每个线程生成不同的随机数。

I need to receive the exact sum for each run, but this will not happen, although I have fixed the seed. I would appreciate it if anyone could point out the problem with this code

首先,你在 rank=omp_get_thread_num(); 上有一个 race-condition,变量 rank 在所有线程之间共享,为了修复你可以声明并行区域内的变量 rank,因此,它对每个线程都是私有的。

 #pragma omp parallel  num_threads(6)  
 {
     int rank=omp_get_thread_num();
     ...
  }

在您的代码中,您不应期望 sum 的值对于不同数量的线程是相同的。为什么?

  1. 因为您要并行添加 doubles

        double sum = 0.0;
        ...
        #pragma omp for reduction (+: sum)
        for (int i = 0; i<N; ++i) {
            //double x = distribution(g);
    
            double x= u(r[rank]); 
            x = (-1.0 / landa) * log(1.0 - x); 
            sum = sum+function(x);
        }
    

    来自 每个计算机科学家都应该知道的关于浮动的知识 点算术 可读:

    Another grey area concerns the interpretation of parentheses. Due to roundoff errors, the associative laws of algebra do not necessarily hold for floating-point numbers. For example, the expression (x+y)+z has a totally different answer than x+(y+z) when x = 1e30, y = -1e30 and z = 1 (it is 1 in the former case, 0 in the latter).

    因此,您由此得出结论,浮点加法不是 关联,以及为什么对于不同数量的线程你可能有不同的 sum 值的原因。

  2. 您正在为每个线程生成不同的随机值

      for (int i = 0; i < 6; i++)
      {
          r[i].split(6,i);
      }
    

    因此,对于不同数量的线程,变量sum 也会得到不同的结果。

正如 jérôme-richard 在评论中指出的那样:

Note that more precise algorithm like the Kahan summation can significantly reduces the rounding issue while being still relatively fast.