Created
September 24, 2020 14:19
-
-
Save EdThePro101/bb37ca6db19db9209b11ae97b9ee03c4 to your computer and use it in GitHub Desktop.
A simple pseudo-random number generator written in Fortran.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
! 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