|
Packit |
67cb25 |
/* rng/slatec.c
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 James Theiler, Brian Gough
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
67cb25 |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
67cb25 |
* the Free Software Foundation; either version 3 of the License, or (at
|
|
Packit |
67cb25 |
* your option) any later version.
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* This program is distributed in the hope that it will be useful, but
|
|
Packit |
67cb25 |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
67cb25 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
67cb25 |
* General Public License for more details.
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
67cb25 |
* along with this program; if not, write to the Free Software
|
|
Packit |
67cb25 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/**
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
* ======================================================================
|
|
Packit |
67cb25 |
* NIST Guide to Available Math Software.
|
|
Packit |
67cb25 |
* Source for module RAND from package CMLIB.
|
|
Packit |
67cb25 |
* Retrieved from TIBER on Fri Oct 11 11:43:42 1996.
|
|
Packit |
67cb25 |
* ======================================================================
|
|
Packit |
67cb25 |
FUNCTION RAND(R)
|
|
Packit |
67cb25 |
C***BEGIN PROLOGUE RAND
|
|
Packit |
67cb25 |
C***DATE WRITTEN 770401 (YYMMDD)
|
|
Packit |
67cb25 |
C***REVISION DATE 820801 (YYMMDD)
|
|
Packit |
67cb25 |
C***CATEGORY NO. L6A21
|
|
Packit |
67cb25 |
C***KEYWORDS RANDOM NUMBER,SPECIAL FUNCTION,UNIFORM
|
|
Packit |
67cb25 |
C***AUTHOR FULLERTON, W., (LANL)
|
|
Packit |
67cb25 |
C***PURPOSE Generates a uniformly distributed random number.
|
|
Packit |
67cb25 |
C***DESCRIPTION
|
|
Packit |
67cb25 |
C
|
|
Packit |
67cb25 |
C This pseudo-random number generator is portable among a wide
|
|
Packit |
67cb25 |
C variety of computers. RAND(R) undoubtedly is not as good as many
|
|
Packit |
67cb25 |
C readily available installation dependent versions, and so this
|
|
Packit |
67cb25 |
C routine is not recommended for widespread usage. Its redeeming
|
|
Packit |
67cb25 |
C feature is that the exact same random numbers (to within final round-
|
|
Packit |
67cb25 |
C off error) can be generated from machine to machine. Thus, programs
|
|
Packit |
67cb25 |
C that make use of random numbers can be easily transported to and
|
|
Packit |
67cb25 |
C checked in a new environment.
|
|
Packit |
67cb25 |
C The random numbers are generated by the linear congruential
|
|
Packit |
67cb25 |
C method described, e.g., by Knuth in Seminumerical Methods (p.9),
|
|
Packit |
67cb25 |
C Addison-Wesley, 1969. Given the I-th number of a pseudo-random
|
|
Packit |
67cb25 |
C sequence, the I+1 -st number is generated from
|
|
Packit |
67cb25 |
C X(I+1) = (A*X(I) + C) MOD M,
|
|
Packit |
67cb25 |
C where here M = 2**22 = 4194304, C = 1731 and several suitable values
|
|
Packit |
67cb25 |
C of the multiplier A are discussed below. Both the multiplier A and
|
|
Packit |
67cb25 |
C random number X are represented in double precision as two 11-bit
|
|
Packit |
67cb25 |
C words. The constants are chosen so that the period is the maximum
|
|
Packit |
67cb25 |
C possible, 4194304.
|
|
Packit |
67cb25 |
C In order that the same numbers be generated from machine to
|
|
Packit |
67cb25 |
C machine, it is necessary that 23-bit integers be reducible modulo
|
|
Packit |
67cb25 |
C 2**11 exactly, that 23-bit integers be added exactly, and that 11-bit
|
|
Packit |
67cb25 |
C integers be multiplied exactly. Furthermore, if the restart option
|
|
Packit |
67cb25 |
C is used (where R is between 0 and 1), then the product R*2**22 =
|
|
Packit |
67cb25 |
C R*4194304 must be correct to the nearest integer.
|
|
Packit |
67cb25 |
C The first four random numbers should be .0004127026,
|
|
Packit |
67cb25 |
C .6750836372, .1614754200, and .9086198807. The tenth random number
|
|
Packit |
67cb25 |
C is .5527787209, and the hundredth is .3600893021 . The thousandth
|
|
Packit |
67cb25 |
C number should be .2176990509 .
|
|
Packit |
67cb25 |
C In order to generate several effectively independent sequences
|
|
Packit |
67cb25 |
C with the same generator, it is necessary to know the random number
|
|
Packit |
67cb25 |
C for several widely spaced calls. The I-th random number times 2**22,
|
|
Packit |
67cb25 |
C where I=K*P/8 and P is the period of the sequence (P = 2**22), is
|
|
Packit |
67cb25 |
C still of the form L*P/8. In particular we find the I-th random
|
|
Packit |
67cb25 |
C number multiplied by 2**22 is given by
|
|
Packit |
67cb25 |
C I = 0 1*P/8 2*P/8 3*P/8 4*P/8 5*P/8 6*P/8 7*P/8 8*P/8
|
|
Packit |
67cb25 |
C RAND= 0 5*P/8 2*P/8 7*P/8 4*P/8 1*P/8 6*P/8 3*P/8 0
|
|
Packit |
67cb25 |
C Thus the 4*P/8 = 2097152 random number is 2097152/2**22.
|
|
Packit |
67cb25 |
C Several multipliers have been subjected to the spectral test
|
|
Packit |
67cb25 |
C (see Knuth, p. 82). Four suitable multipliers roughly in order of
|
|
Packit |
67cb25 |
C goodness according to the spectral test are
|
|
Packit |
67cb25 |
C 3146757 = 1536*2048 + 1029 = 2**21 + 2**20 + 2**10 + 5
|
|
Packit |
67cb25 |
C 2098181 = 1024*2048 + 1029 = 2**21 + 2**10 + 5
|
|
Packit |
67cb25 |
C 3146245 = 1536*2048 + 517 = 2**21 + 2**20 + 2**9 + 5
|
|
Packit |
67cb25 |
C 2776669 = 1355*2048 + 1629 = 5**9 + 7**7 + 1
|
|
Packit |
67cb25 |
C
|
|
Packit |
67cb25 |
C In the table below LOG10(NU(I)) gives roughly the number of
|
|
Packit |
67cb25 |
C random decimal digits in the random numbers considered I at a time.
|
|
Packit |
67cb25 |
C C is the primary measure of goodness. In both cases bigger is better.
|
|
Packit |
67cb25 |
C
|
|
Packit |
67cb25 |
C LOG10 NU(I) C(I)
|
|
Packit |
67cb25 |
C A I=2 I=3 I=4 I=5 I=2 I=3 I=4 I=5
|
|
Packit |
67cb25 |
C
|
|
Packit |
67cb25 |
C 3146757 3.3 2.0 1.6 1.3 3.1 1.3 4.6 2.6
|
|
Packit |
67cb25 |
C 2098181 3.3 2.0 1.6 1.2 3.2 1.3 4.6 1.7
|
|
Packit |
67cb25 |
C 3146245 3.3 2.2 1.5 1.1 3.2 4.2 1.1 0.4
|
|
Packit |
67cb25 |
C 2776669 3.3 2.1 1.6 1.3 2.5 2.0 1.9 2.6
|
|
Packit |
67cb25 |
C Best
|
|
Packit |
67cb25 |
C Possible 3.3 2.3 1.7 1.4 3.6 5.9 9.7 14.9
|
|
Packit |
67cb25 |
C
|
|
Packit |
67cb25 |
C Input Argument --
|
|
Packit |
67cb25 |
C R If R=0., the next random number of the sequence is generated.
|
|
Packit |
67cb25 |
C If R .LT. 0., the last generated number will be returned for
|
|
Packit |
67cb25 |
C possible use in a restart procedure.
|
|
Packit |
67cb25 |
C If R .GT. 0., the sequence of random numbers will start with
|
|
Packit |
67cb25 |
C the seed R mod 1. This seed is also returned as the value of
|
|
Packit |
67cb25 |
C RAND provided the arithmetic is done exactly.
|
|
Packit |
67cb25 |
C
|
|
Packit |
67cb25 |
C Output Value --
|
|
Packit |
67cb25 |
C RAND a pseudo-random number between 0. and 1.
|
|
Packit |
67cb25 |
C***REFERENCES (NONE)
|
|
Packit |
67cb25 |
C***ROUTINES CALLED (NONE)
|
|
Packit |
67cb25 |
C***END PROLOGUE RAND
|
|
Packit |
67cb25 |
DATA IA1, IA0, IA1MA0 /1536, 1029, 507/
|
|
Packit |
67cb25 |
DATA IC /1731/
|
|
Packit |
67cb25 |
DATA IX1, IX0 /0, 0/
|
|
Packit |
67cb25 |
C***FIRST EXECUTABLE STATEMENT RAND
|
|
Packit |
67cb25 |
IF (R.LT.0.) GO TO 10
|
|
Packit |
67cb25 |
IF (R.GT.0.) GO TO 20
|
|
Packit |
67cb25 |
C
|
|
Packit |
67cb25 |
C A*X = 2**22*IA1*IX1 + 2**11*(IA1*IX1 + (IA1-IA0)*(IX0-IX1)
|
|
Packit |
67cb25 |
C + IA0*IX0) + IA0*IX0
|
|
Packit |
67cb25 |
C
|
|
Packit |
67cb25 |
IY0 = IA0*IX0
|
|
Packit |
67cb25 |
IY1 = IA1*IX1 + IA1MA0*(IX0-IX1) + IY0
|
|
Packit |
67cb25 |
IY0 = IY0 + IC
|
|
Packit |
67cb25 |
IX0 = MOD (IY0, 2048)
|
|
Packit |
67cb25 |
IY1 = IY1 + (IY0-IX0)/2048
|
|
Packit |
67cb25 |
IX1 = MOD (IY1, 2048)
|
|
Packit |
67cb25 |
C
|
|
Packit |
67cb25 |
10 RAND = IX1*2048 + IX0
|
|
Packit |
67cb25 |
RAND = RAND / 4194304.
|
|
Packit |
67cb25 |
RETURN
|
|
Packit |
67cb25 |
C
|
|
Packit |
67cb25 |
20 IX1 = AMOD(R,1.)*4194304. + 0.5
|
|
Packit |
67cb25 |
IX0 = MOD (IX1, 2048)
|
|
Packit |
67cb25 |
IX1 = (IX1-IX0)/2048
|
|
Packit |
67cb25 |
GO TO 10
|
|
Packit |
67cb25 |
C
|
|
Packit |
67cb25 |
END
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
**/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
#include <config.h>
|
|
Packit |
67cb25 |
#include <stdlib.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_rng.h>
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static inline unsigned long int slatec_get (void *vstate);
|
|
Packit |
67cb25 |
static double slatec_get_double (void *vstate);
|
|
Packit |
67cb25 |
static void slatec_set (void *state, unsigned long int s);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
typedef struct
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
long int x0, x1;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
slatec_state_t;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static const long P = 4194304;
|
|
Packit |
67cb25 |
static const long a1 = 1536;
|
|
Packit |
67cb25 |
static const long a0 = 1029;
|
|
Packit |
67cb25 |
static const long a1ma0 = 507;
|
|
Packit |
67cb25 |
static const long c = 1731;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static inline unsigned long int
|
|
Packit |
67cb25 |
slatec_get (void *vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
long y0, y1;
|
|
Packit |
67cb25 |
slatec_state_t *state = (slatec_state_t *) vstate;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
y0 = a0 * state->x0;
|
|
Packit |
67cb25 |
y1 = a1 * state->x1 + a1ma0 * (state->x0 - state->x1) + y0;
|
|
Packit |
67cb25 |
y0 = y0 + c;
|
|
Packit |
67cb25 |
state->x0 = y0 % 2048;
|
|
Packit |
67cb25 |
y1 = y1 + (y0 - state->x0) / 2048;
|
|
Packit |
67cb25 |
state->x1 = y1 % 2048;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return state->x1 * 2048 + state->x0;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static double
|
|
Packit |
67cb25 |
slatec_get_double (void *vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
return slatec_get (vstate) / 4194304.0 ;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static void
|
|
Packit |
67cb25 |
slatec_set (void *vstate, unsigned long int s)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
slatec_state_t *state = (slatec_state_t *) vstate;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* Only eight seeds are permitted. This is pretty limiting, but
|
|
Packit |
67cb25 |
at least we are guaranteed that the eight sequences are different */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
s = s % 8;
|
|
Packit |
67cb25 |
s *= P / 8;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->x0 = s % 2048;
|
|
Packit |
67cb25 |
state->x1 = (s - state->x0) / 2048;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static const gsl_rng_type slatec_type =
|
|
Packit |
67cb25 |
{"slatec", /* name */
|
|
Packit |
67cb25 |
4194303, /* RAND_MAX */
|
|
Packit |
67cb25 |
0, /* RAND_MIN */
|
|
Packit |
67cb25 |
sizeof (slatec_state_t),
|
|
Packit |
67cb25 |
&slatec_set,
|
|
Packit |
67cb25 |
&slatec_get,
|
|
Packit |
67cb25 |
&slatec_get_double};
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const gsl_rng_type *gsl_rng_slatec = &slatec_type;
|