随机数种子的可能来源

时间:2012-01-19 02:42:33

标签: random parallel-processing mpi multicore seed

两点 - 首先,示例是在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系统中运行,并且在每个核心上从运行到运行仍然是唯一的?

2 个答案:

答案 0 :(得分: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内调用它有时也很方便,因此当您进行测试时,您可以给它固定值,然后生成可重现的(==可测试的)序列

答案 1 :(得分:0)

系统时间通常以整数形式返回(或至少容易转换为):只需将过程的等级添加到值中,然后使用它来为随机数生成器播种。