Reputation: 1032
I'm working on a class project and am using code from my textbook to generate a random number within my code. However, every time I run the code I get the same output no matter what, which confuses me, so I'm guessing I don't actually understand how the random number generator works. The code is below - it comes with instructions, but I'm not sure exactly what I should be doing to generate a random result each time I run the code?
/* Prime modulus multiplicative linear congruential generator
Z[i] = (630360016 * Z[i-1]) (mod(pow(2,31) - 1)), based on Marse and Roberts'
portable FORTRAN random-number generator UNIRAN. Multiple (100) streams are
supported, with seeds spaced 100,000 apart. Throughout, input argument
"stream" must be an int giving the desired stream number. The header file
lcgrand.h must be included in the calling program (#include "lcgrand.h")
before using these functions.
Usage: (Three functions)
1. To obtain the next U(0,1) random number from stream "stream," execute
u = lcgrand(stream);
where lcgrand is a float function. The float variable u will contain the
next random number.
2. To set the seed for stream "stream" to a desired value zset, execute
lcgrandst(zset, stream);
where lcgrandst is a void function and zset must be a long set to the
desired seed, a number between 1 and 2147483646 (inclusive). Default
seeds for all 100 streams are given in the code.
3. To get the current (most recently used) integer in the sequence being
generated for stream "stream" into the long variable zget, execute
zget = lcgrandgt(stream);
where lcgrandgt is a long function. */
/* Define the constants. */
#define MODLUS 2147483647
#define MULT1 24112
#define MULT2 26143
/* Set the default seeds for all 100 streams. */
static long zrng[] =
{ 1,
1973272912, 281629770, 20006270,1280689831,2096730329,1933576050,
913566091, 246780520,1363774876, 604901985,1511192140,1259851944,
824064364, 150493284, 242708531, 75253171,1964472944,1202299975,
233217322,1911216000, 726370533, 403498145, 993232223,1103205531,
762430696,1922803170,1385516923, 76271663, 413682397, 726466604,
336157058,1432650381,1120463904, 595778810, 877722890,1046574445,
68911991,2088367019, 748545416, 622401386,2122378830, 640690903,
1774806513,2132545692,2079249579, 78130110, 852776735,1187867272,
1351423507,1645973084,1997049139, 922510944,2045512870, 898585771,
243649545,1004818771, 773686062, 403188473, 372279877,1901633463,
498067494,2087759558, 493157915, 597104727,1530940798,1814496276,
536444882,1663153658, 855503735, 67784357,1432404475, 619691088,
119025595, 880802310, 176192644,1116780070, 277854671,1366580350,
1142483975,2026948561,1053920743, 786262391,1792203830,1494667770,
1923011392,1433700034,1244184613,1147297105, 539712780,1545929719,
190641742,1645390429, 264907697, 620389253,1502074852, 927711160,
364849192,2049576050, 638580085, 547070247 };
/* Generate the next random number. */
float lcgrand(int stream)
{
long zi, lowprd, hi31;
zi = zrng[stream];
lowprd = (zi & 65535) * MULT1;
hi31 = (zi >> 16) * MULT1 + (lowprd >> 16);
zi = ((lowprd & 65535) - MODLUS) +
((hi31 & 32767) << 16) + (hi31 >> 15);
if (zi < 0) zi += MODLUS;
lowprd = (zi & 65535) * MULT2;
hi31 = (zi >> 16) * MULT2 + (lowprd >> 16);
zi = ((lowprd & 65535) - MODLUS) +
((hi31 & 32767) << 16) + (hi31 >> 15);
if (zi < 0) zi += MODLUS;
zrng[stream] = zi;
return (zi >> 7 | 1) / 16777216.0;
}
void lcgrandst (long zset, int stream) /* Set the current zrng for stream
"stream" to zset. */
{
zrng[stream] = zset;
}
long lcgrandgt (int stream) /* Return the current zrng for stream "stream". */
{
return zrng[stream];
}
edit: I forgot to actually include the code that is running these functions, and it is this:
float Expon(float mean) /* Exponential variate generation function. */
{
/* Return an exponential random variate with mean "mean". */
return -mean * log(lcgrand(1));
}
Based on my limited understanding, I'm thinking that my issue is arising from lcgrand(1)
but I'm not completely sure.
Upvotes: 0
Views: 1627
Reputation: 16540
I ran your code, via the following:
#include <stdio.h> // printf()
#include <math.h> // logf()
#define MODLUS 2147483647
#define MULT1 24112
#define MULT2 26143
/* Set the default seeds for all 100 streams. */
static long zrng[] =
{ 1,
1973272912, 281629770, 20006270,1280689831,2096730329,1933576050,
913566091, 246780520,1363774876, 604901985,1511192140,1259851944,
824064364, 150493284, 242708531, 75253171,1964472944,1202299975,
233217322,1911216000, 726370533, 403498145, 993232223,1103205531,
762430696,1922803170,1385516923, 76271663, 413682397, 726466604,
336157058,1432650381,1120463904, 595778810, 877722890,1046574445,
68911991,2088367019, 748545416, 622401386,2122378830, 640690903,
1774806513,2132545692,2079249579, 78130110, 852776735,1187867272,
1351423507,1645973084,1997049139, 922510944,2045512870, 898585771,
243649545,1004818771, 773686062, 403188473, 372279877,1901633463,
498067494,2087759558, 493157915, 597104727,1530940798,1814496276,
536444882,1663153658, 855503735, 67784357,1432404475, 619691088,
119025595, 880802310, 176192644,1116780070, 277854671,1366580350,
1142483975,2026948561,1053920743, 786262391,1792203830,1494667770,
1923011392,1433700034,1244184613,1147297105, 539712780,1545929719,
190641742,1645390429, 264907697, 620389253,1502074852, 927711160,
364849192,2049576050, 638580085, 547070247 };
/* Generate the next random number. */
float lcgrand(int stream)
{
long zi, lowprd, hi31;
zi = zrng[stream];
lowprd = (zi & 65535) * MULT1;
hi31 = (zi >> 16) * MULT1 + (lowprd >> 16);
zi = ((lowprd & 65535) - MODLUS) +
((hi31 & 32767) << 16) + (hi31 >> 15);
if (zi < 0) zi += MODLUS;
lowprd = (zi & 65535) * MULT2;
hi31 = (zi >> 16) * MULT2 + (lowprd >> 16);
zi = ((lowprd & 65535) - MODLUS) +
((hi31 & 32767) << 16) + (hi31 >> 15);
if (zi < 0) zi += MODLUS;
zrng[stream] = zi;
return (float)(zi >> 7 | 1) / 16777216.0f; // note cast to `float` and changing `6777216.0` from double to float
}
void lcgrandst (long zset, int stream) /* Set the current zrng for stream
"stream" to zset. */
{
zrng[stream] = zset;
}
long lcgrandgt (int stream) /* Return the current zrng for stream "stream". */
{
return zrng[stream];
}
int main( void )
{
float result = 0.0f;
for( size_t i=0; i<20; i++ )
{
result = lcgrand(1);
result = logf( result ); // note change from `log()` to `logf()`
printf( "%f\n", result );
}
}
The result is:
-0.835640
-0.959694
-0.680949
-0.343732
-1.686887
-0.181990
-0.436622
-0.979359
-1.054951
-0.269755
-0.807537
-0.094184
-0.745967
-0.745367
-0.159959
-0.217918
-0.324280
-0.357220
so the posted code needs a small tweak or two but works correctly
So your application problem is NOT in the posted code
Upvotes: 0
Reputation: 25601
Computers generally cannot generate truly random numbers because they are designed to function deterministically, which is basically the opposite of random. Since the program is in control of all the inputs, and the program does not change, the output cannot be truly random. This is why many computer generated random numbers are referred to as pseudo-random instead of random.
However, despite the fact that the generated numbers cannot be truly random, you can make them less predictable and get a different sequence every time you run the program by introducing some variable that changes every time you run the program. This variable is called a "seed". Many programs will seed their random number generator with a value from the computer's clock, or from something else unpredictable, like a sequence of mouse coordinates from user-provided mouse movement, and possibly the timing between the mouse movements.
Looking at the code you provided, notice that the seeds are all listed as "default seeds", implying that you are expected to override the seed for a particular stream if you don't want that stream to provide the same results every time you run the program.
You may think this is a lot of code to provide a value that could be simply retrieved directly from the mouse or clock. The point of all this code is to make it as difficult as possible for someone to predict what the next number in the sequence will be given the past sampling of results (after all anyone with the same random number generator code could try matching a part of their sequence with your sequence to predict what will be next, which is what random number generators usually are trying to prevent). So the code has to be non-trivial to reduce the likelihood that someone else could use it to predict the next numbers.
Using seeds generated (by the clock or mouse) at run-time is one way to prevent a value from being predictable. But usually it's not practical to constantly be providing random seeds. And because random seeds can sometimes be based on non-random sources (like the clock) you might end up with some predictability in those values alone. So since most sources for seed data are not truly random, you have to make the program spend some time to make it as random and unpredictable as possible. By combining both varying seed values and code that generates numbers based on lots of inputs that would be hard to replicate, you get something that pretty well approximates truly random numbers quickly.
Since this is for a class, I will leave it to you to read the comments on usage point number 2 about how to set the seed.
Upvotes: 3