Random Numbers

Fortran 90 introduced a pseudo-random number generator (PRNG) to the language standard. The routine random_number() returns a single number or an array of numbers from the uniform distribution over the range 0 ≤ x < 1.

GNU Fortran also provides the older extensions srand(), rand() and irand() for backwards compatibility with g77. These routines access an independent PRNG. Additionally, several implementations of the Mersenne Twister (MT19937) have been written in Fortran that return random 32-bit or 64-bit values.

Fortran 90 also added the random_seed() routine that initialises the PRNG. Without seeding, random_number() will always return the same sequence of numbers.

call random_seed([size][, put][, get])
Argument Type Description
size integer The returned minimum size of the array used with put and get (optional).
put integer, dimension(*) The array with seed values to use (optional).
get integer, dimension(*) The used array with seed values (optional).

The GNU Fortran implementation uses the Xoshiro256** pseudo-random number generator, with a period of 2256 – 1. Threads defined by OpenMP directives have their own random number state. The routine random_number() returns either a random real number or an array of random numbers.

call random_number(harvest)
Argument Type Description
harvest real[, dimension(*)] Returned scalar or array with random number(s).

The example initialises the PRNG and fills and array with random numbers:

! random.f90
program main
    implicit none
    real :: r(5)

    call random_seed()
    call random_number(r)

    print '(5(f8.6, " "))', r
end program main

Compile and run the example with:

$ gfortran10 -o random random.f90
$ ./random
0.325213 0.037541 0.241239 0.077683 0.846473

Fortran 2018

In Fortran 2018, the routine random_init() has been added to initialise the state of the PRNG with respect to Co-Arrays.

call random_init(repeatable, image_distinct)
Argument Type Description
repeatable logical If .true., the seed is set to a processor-dependent value that is the same each time random_init() is called from the same instance of program execution. If .false., the seed is set to a processor-dependent value.
image_distinct logical If .true., the seed is set to a processor-dependent value that is distinct from the seed set by a call to random_init() in another process. If .false., the seed depends on which image called random_init().

The routine random_init() allows us to generate the very same random numbers on each initialisation:

! random2018.f90
program main
    implicit none
    real :: r(5)

    call random_init(.true., .true.)
    call random_number(r)
    print '(5(f8.6))', r

    call random_init(.true., .true.)
    call random_number(r)
    print '(5(f8.6))', r
end program main

The array is filled with the same random numbers:

$ gfortran10 -o random2018 random2018.f90
$ ./random2018
0.083351 0.977660 0.863393 0.669568 0.659231
0.083351 0.977660 0.863393 0.669568 0.659231

Seeding

For higher entropy, we can feed a custom seed array to random_seed(), with values obtained from /dev/urandom:

! urandom.f90
program main
    implicit none
    integer :: i, n
    real    :: r

    ! Initialise random number generator.
    call random_seed(size=n)               ! Get size of seed array.
    call random_seed(put=urandom_seed(n))  ! Put seed array into PRNG.

    ! Print some random numbers between 0 and 10,000.
    do i = 1, 10
        call random_number(r)
        print '(i0)', int(r * 10000)
    end do
contains
    function urandom_seed(n, status)
        !! Returns a seed array filled with random values from `/dev/urandom`.
        integer, intent(in)            :: n
        integer, intent(out), optional :: status
        integer                        :: urandom_seed(n)
        integer                        :: fu, rc

        open (access='stream', action='read', file='/dev/urandom', &
              form='unformatted', iostat=rc, newunit=fu)

        if (present(status)) &
            status = rc

        if (rc == 0) &
            read (fu) urandom_seed

        close (fu)
    end function urandom_seed
end program main

The example just outputs 10 random integers:

$ gfortran10 -o urandom urandom.f90
$ ./urandom
985
5948
1279
556
3512
1125
8403
1776
6224
6034