Fortran 90的随机数生成器可信吗,用于蒙特卡罗积分?

10

我编写了一个简短的蒙特卡罗积分算法,用Fortran 90计算一个积分。曾经我使用内置的随机数生成器和Numerical Recipes for Fortran90 Volume 2中介绍的随机数生成器方法ran1解决了一个参数相关积分,并将结果进行比较。

运行同一算法两次,一次调用内置的random_seed()函数,然后始终调用random_number()函数,另一次调用Numerical Recipe书籍提供的ran1()方法。在这两种情况下,我以随机参数10,000次调用该函数,针对参数值q进行累加,然后进入下一个q值并再次调用该函数10,000次等等。

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

如果我增加调用次数,两条曲线都会收敛。但是我想知道:为什么内置的随机数生成器会产生这种平滑性?它是否仍然被普遍建议使用,还是有其他更受推荐的随机数生成器?我认为连续的结果是内置数字生成器“不太”随机的结果。

(我省略了源代码,因为我认为它不会提供太多的信息。如果有人关心,我可以稍后将其提交。)


对于所有Fortran问题,请使用标签[tag:fortran]。如果需要区分版本,请添加版本标签。顺便说一句,因为相同的RNG在后续版本中也存在,如95、2003、2008、2015... - Vladimir F Героям слава
啊,好的,谢谢。我还没有接触过Fortran的较新版本(因为我在某个地方读到它们与Python不兼容),也不知道它们是否在新版本中更改了内置随机数生成器。 - DomiD
我看到有一个被接受的答案,但是我想提出一些问题来挑战自己的理解。在<如果我增加调用次数,两条曲线会收敛>中,您所说的收敛是什么意思? - innoSPG
我的意思是,随着函数评估调用次数的增加,两种方法之间的差异变得越来越小。实际上,积分不应该趋向于一个常数值,而是随着q值的增加而变得越来越小(我正在尝试解决的积分是一些散射形状因子,它应该基本上随着q^(-4)下降)。因此,随着我增加函数调用的数量,越来越多的真实曲线变得可见,基本上两个RNG都会得出相同的结果。我只是想知道为什么在函数调用太少时噪声水平看起来如此不同。 - DomiD
4个回答

10

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

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

在数值数学社区中,Numerical Recipes实际上并不受一些人的欢迎http://www.uwyo.edu/buerkle/misc/wnotnr.html

本网站不是软件推荐网站,但此文章(由roygvib在评论中提供的链接):https://arxiv.org/abs/1005.4117是一篇好的综述文章,其中包括有关不良和良好算法的示例、测试这些算法的方法、如何生成任意数字分布以及如何调用两个C语言示例库(其中一个也可从Fortran调用)的示例。

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


啊,好的,我没看到那个!谢谢,这让一切都更清晰了一些!如果有人想知道,我使用了gfortran版本5.3.1进行编译,它使用了KISS算法。也许,在这种情况下,我会放弃使用数值配方代码,只是看看不同PRNG算法的优缺点,并运行更多测试。谢谢! - DomiD

2
使用的随机数生成器取决于编译器。可能有文档记录,也可能没有。可能会发生变化。为了获得高质量的工作成果,建议使用其他来源的库/源代码。一个网页提供Fortran实现的Mersenne Twister算法: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并已验证其与原始实现相同。这个版本适用于多线程程序。

1
非常感谢这个精彩链接!请注意,作者(Matsumoto和Nishimura)与c++11 MT标准随机数引擎的编码者相同,这无疑保证了一定的质量标准。 - jmon12

1

我同意Vladamir F.的观点,但是...

为了帮助您寻找更好的随机数,可以考虑使用C++中相对较新的C++11伪随机数生成器。其中包括了梅森旋转算法和许多其他算法。这是一个非常完善的系统。我认为您可以通过以下两种方式来测试这些序列:

  • 从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个随机数。

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
许多这些算法都有可用的Fortran实现。 - Vladimir F Героям слава
我不确定那是否有建设性……大多数算法都有许多语言的实现可用。但是,C++的实现最接近“标准”实现,而且编写绑定也不难。 - wawiesel
他显然是用Fortran编程的。我不想替OP说话,但当我用Fortran编程时,更容易调用Fortran实现(而且我确实知道如何调用C ++)。许多算法非常古老,以至于我会称Fortran版本为“标准”。此外,Fortran语言更简单、更清晰。 - Vladimir F Героям слава
原帖想要的是:一堆不同的伪随机数生成器,用他选择的Fortran语言进行测试。我的解决方案是:我知道如果你有gfortran,你就有g++——所以将这些绑定复制/粘贴到你的Fortran代码库中,以访问来自Fortran的C++11随机数生成器。你的解决方案是:去寻找这些Fortran算法,我同意你的观点,它们必须存在。但是找到它们并验证它们的质量所需的时间...我认为这是不可忽略的。但是嘿,各有所好。 :) - wawiesel
嗯,我不确定他想要什么,一堆伪随机数生成器吗?我在问题中没有看到这个。无论如何,在StackOverflow上询问库是不相关的话题。在我的理解中,他是在询问内置生成器的属性以及是否有更好的可用选项,而不是推荐特定的库。 - Vladimir F Героям слава

0

当你进行MC时,PRNG不是一个好的选择,而且这与编程语言无关。对于MC模拟,使用像Random ORG硬件随机数生成器这样的服务总是一个好主意。在书籍Effective Java的第47项中,清楚地展示了有问题的PRNG示例。使用PRNG时,你永远不确定它们的内部实现是否正确。


如果科学工作很严肃,就不应该使用伪随机数生成器。人们这样做并不意味着它是正确的。阅读书中的第47项,你会发现为什么。只要你遇到像书中描述的那种情况之一,就足以破坏你的科学研究。即使在曼哈顿计划中,随机数也是非常谨慎地选择,以便计算足够好。 - Todor Balabanov
1
曼哈顿计划是石器时代,我们现在有了计算机。事实上,存在一些不好的发生器并不意味着所有的都不好。许多发生器是好的,并且经过精心挑选。人们确实会用它们进行科学计算,这是事实。对于大规模超级计算,没有其他方法可行。 - Vladimir F Героям слава
2
这篇文章可能很有用 https://arxiv.org/abs/1005.4117(请参见第8页关于ran2与ran1或0的区别,其中OP正在使用ran1)。MT似乎也非常好。顺便问一下,“Item 47”中的不良PRNG示例是什么(在Java书中)... - roygvib
@roygvib 这非常有用。Todor,请看一下其中关于random.org的估计。*"通常,该服务的成本约为每4百万个随机位1美元。因此,使用10^12个随机数的大规模Monte Carlo模拟将花费250,000美元。"*硬件生成器也是如此。可用的超级计算机根本没有安装它们。没有选择可以做出。除了使用PRNGs之外,没有其他实际选项。 - Vladimir F Героям слава
1
没有其他办法,绝对没有。在超级计算中心没有硬件生成器,或者只有少数计算机有。它们根本不可用。 - Vladimir F Героям слава
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接