Fortran 90的随机数生成器可以信任蒙特卡洛集成吗?

时间:2016-07-08 09:06:58

标签: random fortran integration montecarlo

我已经编写了一个简短的蒙特卡罗积分算法来计算Fortran 90中的积分。我曾经使用内在随机数生成器和随机数生成器方法ran1对比了使用求解积分得到的结果。 Fortran90第2卷的数字配方。

运行相同的算法两次,一次调用内在的random_seed(),然后总是调用random_number(),并且一旦调用了Numerical Recipe书中提供的ran1()方法,我得到的结果是原理相同的形状,但内在的结果与ran1结果形成对比的是连续曲线。在这两种情况下,我使用随机参数调用函数10,000次作为参数值q,添加它然后继续下一个q值并调用函数10,000次等。

结果的比较图片可在此处找到:http://i.imgur.com/gZVFdOP.png

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

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

4 个答案:

答案 0 :(得分:8)

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

您可以研究编译器的手册,找出它对随机数生成器的说法,但每个编译器都可以实现完全不同的算法来生成随机数。

数字数据社区http://www.uwyo.edu/buerkle/misc/wnotnr.html

中的某些人实际上并没有很好地接受数字食谱

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

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

答案 1 :(得分:1)

我同意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)中生成10个随机数。
  5. 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
    

    您可以使用以下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
    

    你的输出应该是这样的(当然有不同的随机数 - 这里的种子是从“熵源”中选择的,例如墙上的时间)。

    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进行测试。

答案 2 :(得分:1)

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

答案 3 :(得分:0)

当您使用MC时,PRNG是一个不好的选择,并且无论使用哪种编程语言都无关紧要。对于MC模拟,使用Random ORGhardware random number generator等服务始终是个好主意。第47项中的书Effective Java清楚地显示了有问题的PRNG的一个例子。对于PRNG,您永远不能确定您的内部实施是否正常。