Monte Carlo 集成可以信任 Fortran 90 的随机数生成器吗?

Can Random Number Generator of Fortran 90 be trusted for Monte Carlo Integration?

我写了一个简短的 monte carlo 积分算法来计算 Fortran 90 中的积分。我曾经将使用固有随机数生成器求解某个参数的积分得到的结果与随机数进行了比较生成器方法 ran1 在 Fortran90 的数值方法第 2 卷中介绍。

运行 两次相同的算法,一次调用内部 random_seed(),然后总是调用 random_number(),一次调用数值配方中提供的 ran1() 方法我得到的书主要是相同的形状,但与 ran1 结果相比,内在结果是一条连续曲线。在这两种情况下,我为参数值 q 调用带有随机参数的函数 10,000 次,添加它然后继续下一个 q 值并调用函数 10,000 次等

可以在此处找到结果的比较图像:

如果我增加调用次数,两条曲线都会收敛。但我想知道:为什么内在随机数生成器会产生这种平滑度?是否仍然普遍建议使用它,还是有其他更建议的 RNG?我想连续的结果是内在数生成器的 "less" 随机性的结果。

(我省略了源代码,因为我不认为它有很多输入。如果有人关心我可以稍后提交。)

对于标准 Fortran 中的伪随机生成器的质量没有任何保证。如果您关心密码学或对随机数 (Monte-Carlo) 敏感的科学的某些特定实现质量,您应该使用一些您可以控制的库。

您可以研究编译器的手册,了解其中关于随机数生成器的内容,但每个编译器都可以实现完全不同的算法来生成随机数。

Numerical Recipes 实际上在数值数学界并没有受到某些人的欢迎http://www.uwyo.edu/buerkle/misc/wnotnr.html

本网站不提供软件推荐,但这篇文章(link 由 roygvib 在评论中给出):https://arxiv.org/abs/1005.4117 是一个很好的评论,包含好的算法和坏算法的示例,方法如何测试它们,如何生成任意数字分布和 C 中两个示例库的调用示例(其中一个也可以从 Fortran 中调用)。

我个人使用这个https://bitbucket.org/LadaF/elmm/src/master/src/rng_par_zig.f90并行PRNG,但我没有测试质量,我个人只需要速度。但这不是软件推荐网站。

我同意 Vladamir F. 但是...

为了帮助您寻求更好的随机数,请考虑最近添加到 C++ 的 C++11 Pseudo Random Number Generators。 Mersenne twister 和许多其他的都在那里。这是一个经过深思熟虑的系统。我看到有两种方法可以测试这些序列:

  • 从 Fortran 系统调用一个小的 C++ 实用程序,为您生成一堆它们或
  • 通过ISO_C_BINDING.
  • 将随机数生成器绑定到Fortran

我更喜欢第二种,因为绑定有点棘手,我准备了一个例子。这些文件是:

  1. cxx11_rand.h 和 cxx11_rand.cpp 定义了对随机数生成器的调用
  2. c_rand.cpp在C函数中调用C++函数
  3. f_rand_M.f90 将 C 函数绑定到 Fortran
  4. f_main.f90 使用模块函数在[0,1].
  5. 中生成10个随机数

cxx11_rand.h

#ifndef CXX11_RAND_H
#define CXX11_RAND_H

void cxx11_init();
double cxx11_rand();

#endif

cxx11_rand.cpp

#include <algorithm>
#include <cstdio>
#include <memory>
#include <random>
#include <set>
#include <vector>
#include <chrono>
#include <thread>
#include <iostream>

#include "cxx11_rand.h"

static std::unique_ptr<std::uniform_real_distribution<>> dist;
static std::unique_ptr<std::mt19937_64> eng;

void cxx11_init(){
    eng = std::unique_ptr<std::mt19937_64>( new std::mt19937_64(std::random_device{}()) );
    dist = std::unique_ptr< std::uniform_real_distribution<> >( new std::uniform_real_distribution<>(0.0,1.0) );
}

double cxx11_rand(){
    return (*dist)( *eng );
}

c_rand.cpp

#include "cxx11_rand.h"

#ifdef __cplusplus
extern "C" {
#endif

void c_init(){
    cxx11_init();
}

double c_rand(){
    return cxx11_rand();
}

#ifdef __cplusplus
}
#endif

f_rand_M.f90

module f_rand_M

    implicit none

    !define fortran interface bindings to C functions
    interface

        subroutine fi_init() bind(C, name="c_init")
        end subroutine

        real(C_DOUBLE) function fi_rand() bind(C, name="c_rand")
            use ISO_C_BINDING, only: C_DOUBLE
        end function

    end interface

contains

    subroutine f_init()
        call fi_init()
    end subroutine

    real(C_DOUBLE) function f_rand()
        use ISO_C_BINDING, only: C_DOUBLE
        f_rand = fi_rand()
    end function

end module

f_main.f90

program main

    use f_rand_M

    implicit none
    integer :: i

    call f_init()
    do i=1,10
        write(*,*)f_rand()
    end do

end program

您可以compile/link使用以下 GNU 命令

echo "compiling objects"
g++ -c --std=c++11 cxx11_rand.cpp c_rand.cpp
gfortran -c f_rand_M.f90

echo "building & executing fortran main"
gfortran f_main.f90 f_rand_M.o c_rand.o cxx11_rand.o -lstdc++ -o f_main.exe
./f_main.exe

你的输出应该是这样的(当然有不同的随机数——这里的种子是从 "source of entropy" 中选择的,例如 wall time)。

compiling objects
building & executing fortran main
  0.47439556226575341
  0.11177335018127127
  0.10417488557661241
  0.77378163596792404
  0.20780793755332663
  0.27951447624366532
  0.66920698086955666
  0.80676663600103105
  0.98028384008440417
  0.88893587108730432

我在 Mac 上使用 GCC 4.9 进行测试。

当你做 MC 时,PRNG 是一个糟糕的选择,它与使用哪种编程语言无关。对于 MC 模拟,使用 Random ORG or hardware random number generator. The book Effective Java 之类的服务始终是个好主意,在项目 47 中,清楚地显示了有问题的 PRNG 示例。使用 PRNG,您永远无法确定您是否接受其内部实现。

使用的特定随机数生成器取决于编译器。也许有记录,也许没有。也许会有所改变。对于高质量的工作,我会使用其他地方的库/源代码。一个网页,其中包含 Mersenne Twister 的 Fortran 实现:http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/FORTRAN/fortran.html. I have used http://theo.phys.sci.hiroshima-u.ac.jp/~ishikawa/PRNG/mt_stream_en.html 并针对原始实现进行了验证。这个版本对多线程程序很有用。