Random MT19937不会通过将种子值保持为常量来复制相同的伪随机序列

Random MT19937不会通过将种子值保持为常量来复制相同的伪随机序列,random,fortran,mersenne-twister,Random,Fortran,Mersenne Twister,我在Fortran 90/95的Monte Carlo模拟中编写了一个检查点函数,我使用的编译器是ifort 18.0.2,在详细说明我使用的伪随机生成器版本之前: A C-program for MT19937, with initialization, improved 2002/1/26. Coded by Takuji Nishimura and Makoto Matsumoto. Code converted to Fortran 95 by Josi Rui Faustino de

我在Fortran 90/95的Monte Carlo模拟中编写了一个检查点函数,我使用的编译器是ifort 18.0.2,在详细说明我使用的伪随机生成器版本之前:

A C-program for MT19937, with initialization, improved 2002/1/26.
Coded by Takuji Nishimura and Makoto Matsumoto.

Code converted to Fortran 95 by Josi Rui Faustino de Sousa
Date: 2002-02-01
有关源代码,请参阅

我的蒙特卡罗模拟代码的一般结构如下所示:

program montecarlo
 call read_iseed(...)
 call mc_subroutine(...)
end 
在读取的
文件中

subroutine read_iseed(...)
  use mt19937

    if (Restart == 'n') then

    call system('od -vAn -N4 -td4 < /dev/urandom > '//trim(IN_ISEED)
    open(unit=7,file=trim(IN_ISEED),status='old')
    read(7,*) i
    close(7)

    !This is only used to initialise the PRNG sequence
    iseed = abs(i)
    else if (Restart == 'y') then

    !Taking seed value from the latest iteration of previous simulation
    iseed = RestartSeed

    endif

    call init_genrand(iseed)
    print *, 'first pseudo-random value ',genrand_real3(), 'iseed ',iseed

    return
end subroutine
输出出乎意料地不是我想的那样,无论如何,
iseed
两次初始化之间的输出是相同的,但是,
genrand\u real3()
输出是不相同的

由于这个意外的结果,我很难在系统的任意状态下恢复模拟,因为模拟没有再现我正在模拟的系统的最新配置状态


我不确定我是否提供了足够的信息,请让我知道这个问题的任何部分是否需要更具体?

从您提供的源代码中(源代码请参见[mt19937]{}),init_genrand不清楚整个状态

有3个关键状态变量:

integer( kind = wi )  :: mt(n)            ! the array for the state vector
logical( kind = wi )  :: mtinit = .false._wi   ! means mt[N] is not initialized
integer( kind = wi )  :: mti = n + 1_wi   ! mti==N+1 means mt[N] is not initialized
第一个是“状态向量的数组”,第二个是确保我们不会从未初始化的数组开始的标志,第三个是一些位置标记,正如我在注释中所述的条件所猜测的那样

查看
子例程init\u genrand(s)
,它设置
mtinit
标志,并从
1
n
填充
mt()
数组。好的

查看
genrand\u real3
它基于
genrand\u int32

查看genrand_int32,它以

if ( mti > n ) then ! generate N words at one time
  ! if init_genrand() has not been called, a default initial seed is used
  if ( .not. mtinit ) call init_genrand( seed_d )
然后进行算术运算,然后开始得到结果:

y = mt(mti)
mti = mti + 1_wi
所以
mti
是“状态数组”中的位置索引,在从生成器读取每个整数后,它将递增1

回到
init\u genrand
-记得吗?它一直在重置阵列
mt()
,但没有将MTI重置回其起始位置
MTI=n+1_wi


我打赌这就是您观察到的现象的原因,因为在使用相同的种子重新初始化后,数组将填充相同的值集,但稍后int32生成器将从不同的起点读取。我怀疑这是有意的,所以这可能是一个容易忽略的小错误。

请在所有Fortran问题中使用tag[tagnfortran]。标记poth fortran90和fortran95没有任何意义,你的问题无论如何都不是版本指定。
init_genrand
如何填充完整的PRNG状态?@francescalus请参见[mt19937ar]{},我的第一次测试证明,它能够通过输入一个恒定的种子值来复制序列,但在我的第二次测试中,当我用相同的种子值重新初始化序列时,它不知何故没有将序列的状态重置回它开始的位置。函数
init\u genrand
在我阅读子例程时,在我看来,
init\u genrand()
通过给定种子值初始化序列的起始位置,
genrand\u real3()
提供了(0,1)中的值。你有没有看到任何分歧?显然我不是唯一一个对这个bug感兴趣的人,这是一个基于mt19937实现检查点重启的已知问题,重置位置索引需要知道调用genrand_real()和genrand_int()的确切总次数,由于这取决于应用程序,因此没有通用的解决方案。但为指出问题而欢呼!
y = mt(mti)
mti = mti + 1_wi