Skip to content

Instantly share code, notes, and snippets.

@EdThePro101
Created September 24, 2020 14:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save EdThePro101/bb37ca6db19db9209b11ae97b9ee03c4 to your computer and use it in GitHub Desktop.
Save EdThePro101/bb37ca6db19db9209b11ae97b9ee03c4 to your computer and use it in GitHub Desktop.
A simple pseudo-random number generator written in Fortran.
! A simple pseudo-random number generator made in Fortran 2018.
!
! The .f95 extension is used only for syntax highlighting.
!
! Compile with:
! gfortran -std=f2018 -Wall -Werror -Wextra prng.f95 -o prng
!
! -----------------------
!
! Equation used:
!
! X_n+1 = (a * X_n + c) mod m
!
! - X_n+1 = The new random number.
! - X_n = The previous random number. If no random number is found, use seed.
! - a = The multiplier.
! - c = The increment.
! - m = The modulus to wrap around.
!
! The maximum random number that can be generated is 999,999,999.
! The minimum random number that can be generated is 0.
!
! a and c is calculated using:
!
! a = n + seed + 128
! c = n + seed + 256
!
! Where n is the current random number been generated, starts at 0.
!
! The seed can be set using `call seed_rand(new_seed)`. Defaults to UTC time.
!
module prng
implicit none
integer(kind = 8), private :: iter = 0
integer(kind = 8), private :: last = 0
integer(kind = 8), private :: seed = 0
logical, private :: rand_seed_called = .false.
integer(kind = 8), parameter :: min_rand = 0
integer(kind = 8), parameter :: max_rand = 999999999
! Get the epoch time.
interface
function get_epoch_time(t) bind(c, name="time")
use, intrinsic :: iso_c_binding, only: c_long
implicit none
integer(kind = c_long), intent(in), value :: t
integer(kind = c_long) :: get_epoch_time
end function get_epoch_time
end interface
contains
! Set the seed.
subroutine seed_rand(new_seed)
use, intrinsic :: iso_c_binding, only: c_long
implicit none
integer(kind = 8), intent(in) :: new_seed
integer(kind = 8) :: newseed
newseed = new_seed
if (new_seed == -1) then
newseed = get_epoch_time(int(0, kind=c_long))
end if
seed = newseed
rand_seed_called = .true.
last = 0
iter = 0
end subroutine seed_rand
! Generate a random number.
function gen_rand()
implicit none
integer(kind = 8) :: a, c, gen_rand
if (.not. rand_seed_called) then
call seed_rand(int(-1, kind = 8))
endif
if (iter == 0) last = seed
a = mod(iter + seed + 128, max_rand)
c = mod(iter + seed + 256, max_rand)
gen_rand = mod(a * last + c, max_rand)
last = gen_rand
iter = iter + 1
end function gen_rand
! Generate a real number in a range.
function gen_real(low, high)
implicit none
real(kind = 8) :: low, high, gen_real
integer(kind = 8) :: rand_num
rand_num = gen_rand()
gen_real = map(dble(rand_num), &
dble(min_rand), dble(max_rand), low, high)
end function gen_real
! Generate a random integer in a range
function gen_int(low, high)
implicit none
integer(kind = 8) :: low, high, rand_num, gen_int
rand_num = gen_rand()
gen_int = nint(map(dble(rand_num), &
dble(min_rand), dble(max_rand), &
dble(low), dble(high)), kind = 8)
end function gen_int
function map(x, a, b, c, d)
implicit none
real(kind = 8) :: x, a, b, c, d, map
map = (x - a) * ((d - c) / (b - a)) + c
end function map
end module prng
! Test program
program test_prng
use prng
implicit none
integer(kind = 8) :: random_num
integer(kind = 8) :: random_int
real(kind = 8) :: random_real
integer :: i
! Generate 10 random numbers. seed is set on the first call of gen_rand.
print "(A)", "Using seed based on timestamp..."
do i = 0, 10
random_num = gen_rand()
print *, random_num
end do
! Generate 10 random numbers using 20 as it's seed.
print "(/, A)", "Using fixed seed..."
call seed_rand(int(20, kind=8))
do i = 0, 10
random_num = gen_rand()
print *, random_num
end do
! Generate 10 random integers between 0 and 69 ;P
print "(/, A)", "Ten random ints..."
call seed_rand(int(-1, kind=8))
do i = 0, 10
random_int = gen_int(int(1, kind=8), int(0, kind = 8))
print *, random_int
end do
! Generate 10 random real numbers between -1 and 1
print "(/, A)", "Ten random real numbers..."
do i = 0, 10
random_real = gen_real(real(-1, kind=8), real(1, kind=8))
print *, random_real
end do
end program test_prng
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment