2012-01-19 85 views
7

两点 - 首先,这个例子在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系统中工作,并且在每个核心上仍然是独一无二的,从运行到运行?

回答

10

如果你看看Katzgrabber的Random Numbers In Scientific Computing: An Introduction(这是一篇关于使用PRNG进行技术计算的细致入微的讨论),同时他们建议使用时间和PID的散列函数来生成种子。从他们的第7.1节:

long seedgen(void) { 
    long s, seed, pid; 

    pid = getpid(); 
    s = time (&seconds); /* get CPU seconds since 01/01/1970 */ 

    seed = abs(((s*181)*((pid-83)*359))%104729); 
    return seed; 
} 
当然

,Fortran语言,这将是类似

function seedgen(pid) 
    use iso_fortran_env 
    implicit none 
    integer(kind=int64) :: seedgen 
    integer, intent(IN) :: pid 
    integer :: s 

    call system_clock(s) 
    seedgen = abs(mod((s*181)*((pid-83)*359), 104729)) 
end function seedgen 

它有时也很方便,能够在规定的时间传递,而不是从seedgen内调用它,所以当你正在测试时,你可以给它固定的值,然后生成一个可重现的(可测试的)序列。

0

系统时间通常返回(或至少很容易转换为)整数类型:只需将进程的排名添加到该值并使用该排序来为随机数生成器播种。

+0

基于对http://stackoverflow.com/questions/1554958/how-different-do-random-seeds-need-to-be的讨论以及答案中引用的文章,只需将等级添加到时间会产生一些不那么伪随机的数字,因为所有的种子都是线性的。但是,如果只有非常好的伪随机,那么时间+等级的方法非常简单,并且与平台无关。 – tpg2114