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

穿精又带淫゛_ 提交于 2019-11-30 13:29:58

There are NO guarantees about the quality of the pseudo random generator in standard Fortran. If you care about some particular quality of implementation for cryptography or science sensitive to random numbers (Monte-Carlo), you should use some library which you have control about.

You can study the manual of your compiler to find out what it says about the random number generator, but every compiler can implement a completely different algorithm to generate random numbers.

Numerical Recipes is actually not well received by some people in the numerical mathematics community http://www.uwyo.edu/buerkle/misc/wnotnr.html

This site is not for software recommendation, but this article (link given by roygvib in a comment): https://arxiv.org/abs/1005.4117 is a good review with examples of bad and good algorithms, methods how to test them, how to generate arbitrary number distributions and examples of calls of two example libraries in C (one of them can be called from Fortran as well).

Personally I use this https://bitbucket.org/LadaF/elmm/src/e732cb9bee3352877d09ae7f6b6722157a819f2c/src/rng_par_zig.f90?at=master parallel PRNG, but I didn't test the quality, I personally just need speed. But this is not a software recommendation site.

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 <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

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.

The particular random number generator used depends on the compiler. Perhaps documented, perhaps not. Perhaps subject to change. For high quality work, I would use library / source code from elsewhere. A webpage with Fortran implementations of the Mersenne Twister: 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 and verified against the original implementation. This version is useful for multi-threaded programs.

PRNG is a bad option when you are doing MC and it does not matter in which programming language. For MC simulations it is always good idea to use service like Random ORG or hardware random number generator. The book Effective Java, in Item 47, clearly shows an example of problematic PRNG. With PRNGs you are never sure that you are OK with their internal implementation.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!