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, 1x))', r
end program main
Compile and run the example with:
$ gfortran13 -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(repeatable=.true., image_distinct=.true.)
call random_number(r)
print '(5(f8.6))', r
call random_init(repeatable=.true., image_distinct=.true.)
call random_number(r)
print '(5(f8.6))', r
end program main
The array is filled with the same random numbers despite re-initialisation:
$ gfortran13 -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 data obtained from
/dev/urandom
:
! urandom.f90
program main
implicit none
integer :: i, n
real :: r(64)
! 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.
! Generate and output values.
call random_number(r)
print '(8(f8.6, 1x))', [ (r(i), i = 1, size(r)) ]
contains
function urandom_seed(n, stat) result(seed)
!! Returns a seed array filled with random values from `/dev/urandom`.
integer, intent(in) :: n
integer, intent(out), optional :: stat
integer :: seed(n)
integer :: fu, rc
open (access='stream', action='read', file='/dev/urandom', &
form='unformatted', iostat=rc, newunit=fu)
if (present(stat)) stat = rc
if (rc == 0) read (fu) seed
close (fu)
end function urandom_seed
end program main
The example program outputs 64 random values:
$ gfortran13 -o urandom urandom.f90
$ ./urandom
0.474257 0.743894 0.944154 0.483623 0.600250 0.315834 0.778809 0.910940
0.538556 0.133123 0.613712 0.619409 0.426239 0.300121 0.989344 0.204518
0.744600 0.119839 0.033373 0.665467 0.795407 0.529277 0.868635 0.488096
0.940779 0.474789 0.680964 0.208645 0.681481 0.886648 0.027110 0.860485
0.826086 0.242641 0.759755 0.102684 0.808770 0.957726 0.359169 0.488995
0.626006 0.464330 0.721632 0.069854 0.248335 0.448519 0.376246 0.805929
0.559363 0.198283 0.137510 0.299755 0.283269 0.067363 0.222422 0.551090
0.661703 0.671904 0.254210 0.674276 0.599526 0.958398 0.852826 0.975271
OpenSSL
The RAND_bytes()
function of
OpenSSL
returns cryptographically strong pseudo-random bytes. We first need to implement
an appropriate ISO C binding interface to the C function. A character array can
then be passed to the function interface to be filled with random values:
! crypto.f90
module openssl
use, intrinsic :: iso_c_binding, only: c_char, c_int
implicit none
interface
! int RAND_bytes(unsigned char *buf, int num)
function c_rand_bytes(buf, num) bind(c, name='RAND_bytes')
import :: c_char, c_int
implicit none
character(kind=c_char), intent(inout) :: buf(*)
integer(kind=c_int), intent(in), value :: num
integer(kind=c_int) :: c_rand_bytes
end function c_rand_bytes
end interface
end module openssl
program main
use :: openssl
implicit none
character :: bytes(128)
integer :: i, rc
rc = c_rand_bytes(bytes, size(bytes))
if (rc /= 1) stop 'Error: RAND_bytes() failed'
print '(16(i4))', [ (ichar(bytes(i)), i = 1, size(bytes)) ]
end program main
The example has to be linked against OpenSSL’s crypto library, using flag
-lcrypto
:
$ gfortran13 -o crypto crypto.f90 -lcrypto
The program then just prints 128 random bytes [0 … 255] to console:
$ ./crypto
98 85 79 19 129 221 19 228 182 145 102 113 161 23 139 216
215 35 144 184 83 234 167 235 85 67 242 145 128 23 227 247
113 13 115 166 170 35 44 59 53 242 231 191 139 157 84 138
140 150 48 182 100 28 241 33 234 111 45 106 246 112 163 211
51 108 237 223 178 65 206 23 126 248 41 152 8 221 54 63
9 242 160 145 217 156 234 214 175 17 197 171 131 10 10 41
215 179 186 3 39 246 139 126 231 219 183 177 35 94 188 184
52 134 189 145 213 186 235 74 203 37 253 69 212 126 2 46
Further Reading
- Y. Masuda: Random numbers
- A. J. Miller: Random Number Generators in Fortran 90
< Sleep | [Index] | Unicode > |