我正在Fortran 90/95的蒙特卡罗模拟中编写一个检查点函数,我正在使用的编译器是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
有关源代码,请参阅mt19937。
我的蒙特卡罗模拟代码的一般结构如下:
program montecarlo
call read_iseed(...)
call mc_subroutine(...)
end
在read_iseed
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
根据我的理解,如果种子值保持不变,PRNG应该能够每次都重现伪随机序列吗?
为了证明这种情况,我使用相同的种子值运行了两次单独的模拟,它们能够重现精确的序列。到目前为止一切都很好!
根据之前的测试,我进一步假设无论在{/ 1>} 一个单独的模拟中init_genrand()
被称为的次数,PRNG也应该能够重现伪 - 随机值序列?所以我对我的read_iseed()
子程序进行了一些修改
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 the previous simulation
iseed = RestartSeed
endif
call init_genrand(iseed)
print *, 'first time initialisation ',genrand_real3(), 'iseed ',iseed
call init_genrand(iseed)
print *, 'second time initialisation ',genrand_real3(), 'iseed ',iseed
return
end subroutine
输出令人惊讶的不是我认为的情况,在两次初始化之间,所有iseed
输出都是相同的,但genrand_real3()
输出不相同。
由于这种意想不到的结果,我努力在系统的任意状态下恢复模拟,因为模拟没有重现我正在模拟的系统的最新配置状态。
我不确定我是否提供了足够的信息,如果这个问题的任何部分需要更具体,请告诉我?
答案 0 :(得分:1)
从您提供的源代码(参见[mt19937] {http://web.mst.edu/~vojtat/class_5403/mt19937/mt19937ar.f90}获取源代码。),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
第一个是状态向量&#34;的#34;数组,第二个是确保我们不用未初始化数组开始的标志,第三个是一个位置标记,就像我根据评论中陈述的条件猜测。
查看subroutine init_genrand( s )
,它会设置mtinit
标记,并将mt()
数组从1
填充到n
。好的。
根据genrand_real3
查看genrand_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
是&#39;状态数组&#39;中的位置索引,并且在从生成器读取的每个整数后递增1。
回到init_genrand
- 还记得吗?它已重置数组mt()
,但尚未将MTI 重置为其开始mti = n + 1_wi
。
我敢打赌这是你所观察到的现象的原因,因为在使用相同的种子重新初始化之后,数组将被填充相同的值集,但是后来int32生成器将从不同的起点。我怀疑它是有意的,所以它可能是一个容易被忽视的小虫子。