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

前端 未结 4 1631
悲哀的现实
悲哀的现实 2021-01-02 07:28

I have written a short monte carlo integration algorithm to calculate an integral in Fortran 90. I once compared the result obtained by solving the integral with respect to

4条回答
  •  野趣味
    野趣味 (楼主)
    2021-01-02 07:31

    I agree with Vladamir F. But...

    To help you in your quest for better random numbers, consider the fairly recent addition to C++, C++11 Pseudo Random Number Generators. Mersenne twister and lots of others are there. It's a pretty well-thought out system. I see two ways you could test those sequences:

    • make a system call from Fortran to a little C++ utility that generates a bunch of them for you or
    • bind the random number generators to Fortran through ISO_C_BINDING.

    I prefer the second and because the bindings are a little tricky, I have prepared an example. The files are:

    1. cxx11_rand.h and cxx11_rand.cpp which define calls to the random number generator
    2. c_rand.cpp which calls the C++ functions in C functions
    3. f_rand_M.f90 which binds the C functions to Fortran
    4. f_main.f90 which uses the module functions to generate 10 random numbers in [0,1).

    cxx11_rand.h

    #ifndef CXX11_RAND_H
    #define CXX11_RAND_H
    
    void cxx11_init();
    double cxx11_rand();
    
    #endif
    

    cxx11_rand.cpp

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include "cxx11_rand.h"
    
    static std::unique_ptr> dist;
    static std::unique_ptr eng;
    
    void cxx11_init(){
        eng = std::unique_ptr( 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
    

    You can compile/link with the following GNU commands

    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
    

    Your output should look like this (with different random numbers of course--the seed here was chosen from a "source of entropy", e.g. 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
    

    I used GCC 4.9 on a Mac for testing.

提交回复
热议问题