Listing 2 A portable and reasonably fast multiplicative random number generator
/* Listing 2 rand_por[t].c see L'Ecuyer - Comm. of the ACM, Oct. 1990, vol. 33. Numerical Recipes in C, 2nd edition, pp. 278-86 L'Ecuyer and Cote, ACM Transactions on Mathematical Software, March 1991 Russian peasant algorithm -- Knuth, vol. II, pp. 442-43 Copyright (c) 1994, 1995 by Gerald P. Dwyer, Jr. */ #include <time.h> #include <stdlib.h> #include <limits.h> #include <assert.h> #define TESTING #define TRUE (-1) #define FALSE 0 long init_rand_port(long seed) ; long get_init_rand_port(void); long genr_rand_port(long init_rand) ; long rand_port(void) ; double rand_rect_port(void) ; long skip_ahead(long a, long init_rand, long modulus, long skip) ; long mult_mod(long a, long x, long modulus) ; #define MOD 2147483647L /* modulus for generator */ #define MULT 41358L /* multiplier */ /* modulus = mult*quotient + remainder */ #define Q 51924L /* int(modulus / multiplier) */ #define R 10855L /* remainder */ #define MAX_VALUE (MOD-1) #define EXP_VAL 1285562981L /* value for 10,000th draw */ #define IMPOSSIBLE_RAND (-1) #define STARTUP_RANDS 16 /* throw away this number of initial random numbers */ static long rand_num = IMPOSSIBLE_RAND ; /* initialize random number generator with seed */ long init_rand_port(long seed) { extern long rand_num ; int i ; if (seed < 1 || seed > MAX_VALUE) /* if seed out of range */ seed = get_init_rand_port() ; /* get seed */ rand_num = seed ; for (i = 0; i < STARTUP_RANDS; i++) /* and throw away */ rand_num = genr_rand_port(rand_num) ; /* some initial ones */ return seed ; } /* get a long initial seed for gererator assumes that rand returns a short integer */ long get_init_rand_port(void) { long seed ; srand((unsigned int)time(NULL)); /* initialize system generator */ do { seed = ((long)rand())*rand() ; seed += ((long)rand())*rand() ; } while (seed > MAX_VALUE) ; assert (seed > 0) ; return seed ; } /* generate the next value in sequence from generator uses approximate factoring residue = (a * x) mod modulus = a*x - [(a*x)/modulus]*modulus where [(a*x)/modulus] = integer less than or equal to (a*x)/modulus approximate factoring avoids overflow associated with a*x and uses equivalence of above with residue = a * (x - q * k) - r* k + (k-k1) * modulus where modulus = a * q + r q = [modulus/a] k = [x/q] (= [ax/aq]) k1 = [a*x/modulus] assumes a, m > 0 0 < init_rand < modulus a * a <= modulus [a*x/a*q]-[a*x/modulus] <= 1 (for only one addition of modulus below) */ long genr_rand_port(long init_rand) { long k, residue ; k = init_rand / Q ; residue = MULT * (init_rand - Q * k) - R * k ; if (residue < 0) residue += MOD ; assert(residue >= 1 && residue <= MAX_VALUE) ; return residue; } /* get a random number */ long rand_port(void) { extern long rand_num; /* if not initialized, do it now */ if (rand_num == IMPOSSIBLE_RAND) { rand_num = 1 ; init_rand_port(rand_num) ; } rand_num = genr_rand_port(rand_num) ; return rand_num; } /* generates a value on (0,1) with mean of .5 range of values is [1/(MAX_VALUE+1), MAX_VALUE/(MAX_VALUE+1)] to get [0,1], use (double)(rand_port()-1)/(double)(MAX_VALUE-1) */ double rand_rect_port(void) { return (double)rand_port()/(double)(MAX_VALUE+1) ; } /* skip ahead in recursion residue = (a^skip * init) mod modulus Use Russian peasant algorithm */ long skip_ahead(long a, long init_rand, long modulus, long skip) { long residue = 1 ; if (init_rand < 1 || init_rand > modulus-1 || skip < 0) return -1 ; while (skip > 0) { if (skip % 2) residue = mult_mod(a, residue, modulus) ; a = mult_mod(a, a, modulus) ; skip >>= 1 ; } residue = mult_mod(residue, init_rand, modulus) ; assert(residue >= 1 && residue <= modulus-1) ; return residue ; } /* calculate residue = (a * x) mod modulus for arbitrary a and x without overflow assume 0 < a < modulus and 0 < x < modulus use Russian peasant algorithm followed by approximate factoring */ long mult_mod(long a, long x, long modulus) { long q, r, k, residue; residue = -modulus ; /* to avoid overflow on addition */ while (a > SHRT_MAX) { /* use Russian Peasant to reduce a */ if (a % 2) { residue += x; if (residue > 0) residue -= modulus ; } x += (x - modulus) ; if (x < 0) x += modulus ; a >>=1; } /* now apply approximate factoring to a and compute (a * x) mod modulus */ q = modulus / a ; r = modulus - a * q ; k = x / q ; x = a * (x - q * k) - r * k ; while (x < 0) x += modulus ; /* add result to residue and take mod */ residue += x ; if (residue < 0) /* undo initial subtraction if necessary */ residue += modulus ; assert(residue >= 1 && residue <= modulus-1) ; return residue ; } #if defined(TESTING) /* Test the generator */ #include <stdio.h> void main(void) { long seed ; int i ; seed = init_rand_port(1); printf("Seed for random number generator is %ld\n", seed) ; i = STARTUP_RANDS ; /* threw away STARTUP_RANDS */ do { rand_port() ; 1++; } while (i < 9999) ; printf("On draw 10000, random number should be %ld\n", EXP_VAL) ; printf("On draw %d, random number is %ld\n", i+1, rand_port()) ; } #endif /* TESTING */ /* End of File */