两点 - 首先,这个例子在Fortran中,但我认为它应该适用于任何语言;其次,内置的随机数发生器并不是真正的随机数和其他生成器,但我们并不愿意将它们用于我们正在做的事情。随机数种子的可能来源
关于随机种子的大多数讨论都承认,如果程序没有在运行时对其进行种子处理,那么在编译时会生成种子。因此,每次运行程序时都会生成相同的数字序列,这对于随机数字并不合适。克服这个问题的一个方法是用系统时钟给随机数发生器播种。
但是,当在多核机器上与MPI并行运行时,系统时钟方法对于我们产生了相同类型的问题。当序列从运行变为运行时,所有处理器都获得相同的系统时钟,因此具有相同的随机种子和相同的序列。
因此考虑下面的示例代码:
PROGRAM clock_test
IMPLICIT NONE
INCLUDE "mpif.h"
INTEGER :: ierr, rank, clock, i, n, method
INTEGER, DIMENSION(:), ALLOCATABLE :: seed
REAL(KIND=8) :: random
INTEGER, PARAMETER :: OLD_METHOD = 0, &
NEW_METHOD = 1
CALL MPI_INIT(ierr)
CALL MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr)
CALL RANDOM_SEED(SIZE=n)
ALLOCATE(seed(n))
DO method = 0, 1
SELECT CASE (method)
CASE (OLD_METHOD)
CALL SYSTEM_CLOCK(COUNT=clock)
seed = clock + 37 * (/ (i - 1, i = 1, n) /)
CALL RANDOM_SEED(put=seed)
CALL RANDOM_NUMBER(random)
WRITE(*,*) "OLD Rank, dev = ", rank, random
CASE (NEW_METHOD)
OPEN(89,FILE='/dev/urandom',ACCESS='stream',FORM='UNFORMATTED')
READ(89) seed
CLOSE(89)
CALL RANDOM_SEED(put=seed)
CALL RANDOM_NUMBER(random)
WRITE(*,*) "NEW Rank, dev = ", rank, random
END SELECT
CALL MPI_BARRIER(MPI_COMM_WORLD, ierr)
END DO
CALL MPI_FINALIZE(ierr)
END PROGRAM clock_test
,当我的工作站上用2个内核运行,得出:
OLD Rank, dev = 0 0.330676306089146
OLD Rank, dev = 1 0.330676306089146
NEW Rank, dev = 0 0.531503215980609
NEW Rank, dev = 1 0.747413828750221
所以,我们从/dev/urandom
阅读种子克服了时钟问题代替。这样每个核心都有自己的随机数。
还有哪些其他种子方法可以在多核MPI系统中工作,并且在每个核心上仍然是独一无二的,从运行到运行?
基于对http://stackoverflow.com/questions/1554958/how-different-do-random-seeds-need-to-be的讨论以及答案中引用的文章,只需将等级添加到时间会产生一些不那么伪随机的数字,因为所有的种子都是线性的。但是,如果只有非常好的伪随机,那么时间+等级的方法非常简单,并且与平台无关。 – tpg2114