Возможные источники случайных чисел

Два момента - во-первых, пример на Фортране, но я думаю, что он должен быть применим для любого языка; во-вторых, встроенные генераторы случайных чисел не являются на самом деле случайными, и существуют другие генераторы, но мы не заинтересованы в их использовании для того, что мы делаем.

Большинство дискуссий о случайных начальных числах признают, что если программа не запускает их во время выполнения, то начальное число генерируется во время компиляции. Таким образом, при каждом запуске программы генерируется одна и та же последовательность чисел, что не подходит для случайных чисел. Один из способов преодолеть это - заполнить генератор случайных чисел системными часами.

Однако при параллельной работе с 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 и при этом будут уникальными для каждого ядра от запуска к запуску?


person tpg2114    schedule 19.01.2012    source источник


Ответы (2)


Если вы посмотрите на Случайные числа в научных вычислениях: Введение Кацграббера (это отличное, ясное обсуждение всех нюансов и выходы из использования 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;
}

конечно, в Фортране это было бы что-то вроде

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, чтобы при тестировании вы могли дать ему фиксированные значения, которые затем генерируют воспроизводимую (== тестируемую) последовательность.

person Jonathan Dursi    schedule 19.01.2012

Системное время обычно возвращается в целочисленном типе (или, по крайней мере, легко преобразуется в него): просто добавьте ранг процесса к значению и используйте его для заполнения генератора случайных чисел.

person suszterpatt    schedule 19.01.2012
comment
На основе обсуждения stackoverflow.com/questions/1554958/ и в статье, цитируемой в ответе, просто добавление ранга ко времени генерирует некоторые не очень псевдослучайные числа, потому что все начальные числа будут линейными. Но если допустим только очень псевдослучайный подход, тогда подход «время + ранг» очень прост и не зависит от платформы. - person tpg2114; 19.01.2012