-
Notifications
You must be signed in to change notification settings - Fork 12
/
m_rand_knuth.f90
155 lines (112 loc) · 3.2 KB
/
m_rand_knuth.f90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
MODULE m_rand_knuth
! Random number generator
! Code converted using TO_F90 by Alan Miller
! Date: 2000-09-10 Time: 16:37:48
! Latest revision - 16 January 2003
! FORTRAN 77 version of "ran_array"
! from Seminumerical Algorithms by D E Knuth, 3rd edition (1997)
! including the MODIFICATIONS made in the 9th printing (2002)
! ********* see the book for explanations and caveats! *********
! Author: Steve Kifowit
! http://ourworld.compuserve.com/homepages/steve_kifowit
! with modifications by Alan Miller to rnarry and rnstrt based upon
! Knuth's code.
! For Donald Knuth's Fortran 77 versions, go to:
! http://www-cs-faculty.stanford.edu/~knuth/programs
! Look for frng.f and frngdb.f
IMPLICIT NONE
INTEGER, PARAMETER :: kk=100, ll=37, mm=2**30, tt=70, kkk=kk+kk-1
INTEGER, SAVE :: ranx(kk)
CONTAINS
SUBROUTINE rand_knuth(u, n)
! Generate an array of n real values between 0 and 1.
REAL , INTENT(OUT) :: u(:)
INTEGER, INTENT(IN) :: n
! Local array
INTEGER :: aa(n)
CALL rnarry(aa, n)
u(1:n) = SCALE( REAL(aa), -30)
RETURN
END SUBROUTINE rand_knuth
SUBROUTINE rnarry(aa, n)
! Generate an array of n integers between 0 and 2^30-1.
INTEGER, INTENT(OUT) :: aa(:)
INTEGER, INTENT(IN) :: n
! Local variables
INTEGER :: j
aa(1:kk) = ranx(1:kk)
DO j = kk + 1, n
aa(j) = aa(j-kk) - aa(j-ll)
IF (aa(j) < 0) aa(j) = aa(j) + mm
END DO
DO j=1,ll
ranx(j) = aa(n+j-kk) - aa(n+j-ll)
IF (ranx(j) < 0) ranx(j) = ranx(j) + mm
END DO
DO j=ll+1,kk
ranx(j) = aa(n+j-kk) - ranx(j-ll)
IF (ranx(j) < 0) ranx(j) = ranx(j) + mm
END DO
RETURN
END SUBROUTINE rnarry
SUBROUTINE rand_knuth_start(seed)
! Initialize integer array ranx using the input seed.
INTEGER*8, INTENT(IN) :: seed
! Local variables
INTEGER :: x(kkk), j, ss, sseed, t
IF (seed < 0) THEN
sseed = mm - 1 - MOD(-1-seed, mm)
ELSE
sseed = MOD(seed, mm)
END IF
ss = sseed - MOD(sseed,2) + 2
DO j=1, kk
x(j) = ss
ss = ISHFT(ss, 1)
IF (ss >= mm) ss = ss - mm + 2
END DO
x(kk+1:kkk) = 0
x(2) = x(2)+1
ss = sseed
t = tt - 1
10 DO j=kk, 2, -1
x(j+j-1) = x(j)
END DO
DO j = kkk, kk + 1, -1
x(j-(kk-ll)) = x(j-(kk-ll)) - x(j)
IF (x(j-(kk-ll)) < 0) x(j-(kk-ll)) = x(j-(kk-ll)) + mm
x(j-kk) = x(j-kk) - x(j)
IF (x(j-kk) < 0) x(j-kk) = x(j-kk) + mm
END DO
IF (MOD(ss,2) == 1) THEN
DO j=kk, 1, -1
x(j+1) = x(j)
END DO
x(1) = x(kk+1)
x(ll+1) = x(ll+1) - x(kk+1)
IF (x(ll+1) < 0) x(ll+1) = x(ll+1) + mm
END IF
IF (ss /= 0) THEN
ss = ISHFT(ss, -1)
ELSE
t = t - 1
END IF
IF (t > 0) GO TO 10
DO j=1, ll
ranx(j+kk-ll) = x(j)
END DO
DO j=ll+1,kk
ranx(j-ll) = x(j)
END DO
DO j = 1, 10
CALL rnarry(x,kkk)
END DO
RETURN
END SUBROUTINE rand_knuth_start
! Initialization subroutine
subroutine rand_knuth_init(seed1)
integer*8 :: seed1
call rand_knuth_start(seed1)
return
end subroutine rand_knuth_init
END MODULE m_rand_knuth