【问题标题】:Possible sources for random number seeds随机数种子的可能来源
【发布时间】:2012-02-13 18:41:44
【问题描述】:

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

【问题讨论】:

    标签: random parallel-processing mpi multicore seed


    【解决方案1】:

    如果您查看 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 中调用它,这样当您进行测试时,您可以为其提供固定值,然后生成可重现(== 可测试)的序列。

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 基于stackoverflow.com/questions/1554958/… 上的讨论和答案中引用的文章,只需将排名添加到时间会生成一些不那么伪随机数,因为所有种子都是线性的。但是,如果只有非常伪随机是可以的,那么时间+排名方法非常简单且与平台无关。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-04
      • 2013-05-28
      • 1970-01-01
      • 2014-10-03
      • 1970-01-01
      相关资源
      最近更新 更多