This question already has answers here:
What‘s the difference between srand(1) and srand(0)
(7 answers)
Closed 8 years ago.
srand(0) and srand(1) give the same results. srand(2), srand(3), etc. give different results.
Any reason why seed = 0 and seed = 1 yield the same random sequence?
Can't find an explanation in the man page. Only that if a seed is not provided, seed = 1 is used.
Thanks.
Within the glibc sources for srandom_r (which is aliased to srand), line 179:
/* We must make sure the seed is not 0. Take arbitrarily 1 in this case. */
if (seed == 0)
seed = 1;
It's just an arbitrary decision basically.
Depends on compiler!
srand(0);
int a=rand(),b=rand();
srand(1);
int c=rand(),d=rand();
VC 2005 result:
a 0x00000026 int
b 0x00001e27 int
c 0x00000029 int
d 0x00004823 int
This is an implementation dependent behaviour.
For instance, POSIX.1-2001 gives the following example of an implementation of rand() and srand()
static unsigned long next = 1;
/* RAND_MAX assumed to be 32767 */
int myrand(void) {
next = next * 1103515245 + 12345;
return((unsigned)(next/65536) % 32768);
}
void mysrand(unsigned seed) {
next = seed;
}
Now, if you use this implementation you will end up with:
0
16838
for srand(0) and srand(1) respectively.
ref.:
http://linux.die.net/man/3/rand
I ran into a quite similar problem before, where rand() yielded different sequences for the same seed across different platforms. Lesson learned, portable code should implement his own PRNG.
The function srand() is used to initialize the pseudo-random number generator by passing the argument seed.
So if the seed is set to 1 then the generator is reinitialized to its initial value. Then it will produce the results as before any call to rand and srand.
so srand(1) actually represent the result srand(0).
Related
This question already has answers here:
Rand() % 14 only generates the values 6 or 13
(3 answers)
Closed 1 year ago.
I have a problem, I want to use rand() to get a random number between 0 and 6, but it always gives me 4 at each run, even when I call srand(time(NULL))
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
int main(void)
{
srand(time(NULL));
int rd = rand() % 7;
printf("%d\n", rd);
return (0);
}
output is 4 at each run
There are two fundamental problems with your code which, in combination, produce the curious result you're experiencing.
Almost anyone will warn you about the use of the rand() interface. Indeed, the Mac OS manpage itself starts with a warning:
$ man rand
NAME
rand, srand, sranddev, rand_r -- bad random number generator
Yep, it's a bad random number generator. Bad random number generators can be hard to seed, among other problems.
But speaking of seeding, here's another issue, perhaps less discussed but nonetheless important:
Do not use time(NULL) to seed your random number generator.
The linked answer goes into more detail about this, but the basic issue is simple: the value of time(NULL) changes infrequently (if frequently is measured in nanoseconds), and doesn't change much when it changes. So not only are you relying on the program to not be run very often (or at least less than once per second), you're also depending on the random number generator to produce radically different values from slightly different seeds. Perhaps a good random number generator would do that, but we've already established that rand() is a bad random number generator.
OK, that's all very general. The specific problem is somewhat interesting, at least for academic purposes (academic, since the practicial solution is always "use a better random number generator and seed it with a good random seed"). The precise problem here is that you're using rand() % 7.
That's a problem because what the Mac OS / FreeBSD implementation of rand() does is to multiply the seed by a multiple of 7. Because that product is reduced modulo 232 (which is not a multiple of 7), the value modulo 7 of the first random number produced by slowly incrementing seeds will eventually change, but it will have to wait until the amount of the overflow changes.
Here's a link to the code. The essence is in these three lines:
hi = *ctx / 127773;
lo = *ctx % 127773;
x = 16807 * lo - 2836 * hi;
which, according to a comment, "compute[s] x = (7^5 * x) mod (2^31 - 1) without overflowing 31 bits." x is the value which will eventually be returned (modulo 232) and it is also the next seed. *ctx is the current seed.
16807 is, as the comment says, 75, which is obviously divisible by 7. And 2836 mod 7 is 1. So by the rules of modular arithmetic:
x mod 7 = (16807 * lo) mod 7 - (2836 * hi) mod 7
= 0 - hi mod 7
That value only depends on hi, which is seed / 127773. So hi changes exactly once every 127773 ticks. Since the result of time(NULL) is in seconds, that's one change in 127773 seconds, which is about a day and a half. So if you ran your program once a day, you'd notice that the first random number is sometimes the same as the previous day and sometimes one less. But you're running it quite a bit more often than that, even if you wait a few seconds between runs, so you just see the same first random number every time. Eventually it will tick down and then you'll see a series of 3s instead of 4s.
As mentioned by #rici, the problem is caused by the poor implementation of rand(). The man page for srand() recommends using arc4random() instead. Alternatively, you could try seeding with a value taken directly from /dev/urandom as follows:
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int seed;
FILE *f = fopen("/dev/urandom", "r");
fread(&seed, sizeof(int), 1, f);
srand(seed);
fclose(f);
/* Should be a lot more unpredictable: */
printf("%d\n", rand() % 7);
return (0);
}
I'm trying to generate a random-number sequence with rand().
I have something like this:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int Random(int min, int max)
{
/* returns a random integer in [min, max] */
double uniform; // random variable from uniform distribution of [0, 1]
int ret; // return value
srand((unsigned int)clock());
uniform = rand() / (double)RAND_MAX;
ret = (int)(uniform * (double)(max - min)) + min;
return ret;
}
int main(void)
{
for(int i=0; i<10; i++)
printf("%d ", Random(0, 100));
printf("\n");
return 0;
}
It made different results when executed on macOS v10.14 (Mojave) and Ubuntu 18.04 (Bionic Beaver).
It works on Ubuntu:
76 42 13 49 85 7 43 28 15 1
But not on macOS:
1 1 1 1 1 1 1 1 1 1
Why doesn't it work well on macOS? Is there something different in random number generators?
I'm a Mac user. To generate random numbers I initialise the seed like this:
srand(time(NULL));
Plus, try initialising it in your main.
If reproducible "random" numbers are something you care about, you should avoid the rand function. The C standard doesn't specify exactly what the sequence produced by rand is, even if the seed is given via srand. Notably:
rand uses an unspecified random number algorithm, and that algorithm can differ between C implementations, including versions of the same standard library.
rand returns values no greater than RAND_MAX, and RAND_MAX can differ between C implementations.
Instead, you should use an implementation of a pseudorandom number generator with a known algorithm, and you should also rely on your own way to transform pseudorandom numbers from that algorithm into the numbers you desire. (For many ways to do so, see my page on sampling algorithms. Note that there are other things to consider when reproducibility is important.)
See also the following:
Does Python have a function to mimic the sequence of C's rand()?
Why is the use of rand() considered bad?
How predictable is the result of rand() between individual systems?
rand is obsolete in Mac. Use random() instead.
This question already has answers here:
srand() — why call it only once?
(7 answers)
Closed 4 years ago.
I'm trying to generate 10,000 random numbers in a row in C and am having trouble getting random or even randomish results using the pseudo RNG. I used modulus in a way that I think should create uniformity, which it does, but the results are equivalent to 0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3 etc. when run in a loop in another function calling RNG(4).
int RNG(int n) {
int range = RAND_MAX - (RAND_MAX % n);
srand(time(NULL));
int x = rand();
while (x > range) {
x = rand();
}
return x % n;
}
Any way to get it closer to 1,3,2,0,2,3,1,0,0,3,2,0,1 etc. would be appreciated!
Thank you!
EDIT: Thanks for the responses everyone! Moved the seeding to the start of the function calling RNG and everything is dandy now!
Do not call srand every time you want to generate a number. srand initializes the pseudo-random number generator and is intended to be called just once at the start of your program, or when you want to reset the generator. By resetting it every time, you are forcing rand to generate the same numbers every time you call it within each second on the clock.
Do not use x % n to reduce the number to a desired range. Old implementations of rand are notoriously bad and have patterns in the low bits. Instead, use x / ((RAND_MAX+1u) / n).
The code int range = RAND_MAX - (RAND_MAX % n); is flawed. Suppose n is 4 and RAND_MAX is 7, meaning rand returns 0 to 7. This code sets range to 4, and then while (x > range) x = rand(); discards 5, 6, and 7, while it retains 4. There are two bugs here: The code keeps the five values 0, 1, 2, 3, and 4, which is a mismatch to (not a multiple of) the desired range of 4, and it unnecessarily discards values. If we had kept 4, 5, 6, and 7, we would have a match. You could use:
unsigned range = (RAND_MAX + 1u) - ((RAND_MAX + 1u) % n);
and:
while (x >= range) x = rand();
If you are using C++, switch to using std::uniform_int_distribution. If you are using C, check the quality of rand in your implementation or switch to another generator such as the POSIX srandom and random.
As noted elsewhere, the fix to the repeated numbers is to move the call to srand(time(NULL)) outside this function and call it only once per program at the beginning.
As for why you're getting repeated numbers: The function is being called several times per second. Each time the function executes in a given second, time(NULL) returns the same number, which this code uses to seed the random number generator.
The sequence of random numbers generated from a particular seed will always be the same. This code takes the first number from that sequence, which is always the same for one second, until time(NULL) returns a new value.
I understand that what's passed into srand() as an argument will be used as the seed for calls to rand() that happen afterward. Generally time(NULL) is passed in.
But if you only call srand once, and then you have rand being called continuously in a loop, how are the numbers different from each other? The seed was the value returned from time(NULL)...but ONLY that one specific return value is being used. So how are different numbers being generated?
I don't know anything about threading in C. But is srand using threads and a different seed each time so that subsequent calls to rand actually do return random numbers? I noticed that when I pass a static number to srand instead of time(NULL), the rand is still generating random numbers.
The draft C99 standard provides a sample implementation in section 7.20.2.2 The srand function that will probably clear up how it work for you:
EXAMPLE The following functions define a portable implementation of rand and srand.
static unsigned long int next = 1;
int rand(void) // RAND_MAX assumed to be 32767
{
next = next * 1103515245 + 12345;
return (unsigned int)(next/65536) % 32768;
}
void srand(unsigned int seed)
{
next = seed;
}
srand set the static variable next which is then set to new value in rand and then subsequent calls to rand will pick up with the previous value of next.
For example if we used srand(1) then the sequence would look like this as we make calls to rand:
next = 1
next = 1 * 1103515245 + 12345 = 1103527590
next = 1103527590 * 1103515245 + 12345 = 1217759518843121895
next = 1217759518843121895 * 1103515245 + 12345 = 15426401326682203284
As HostileFork points out, the numbers are not truly random but are Pseudorandom, which says:
also known as a deterministic random bit generator (DRBG),[1] is an
algorithm for generating a sequence of numbers whose properties
approximate the properties of sequences of random numbers. The
PRNG-generated sequence is not truly random, because it is completely
determined by a relatively small set of initial values, called the
PRNG's seed (which may include truly random values). Although
sequences that are closer to truly random can be generated using
hardware random number generators, pseudorandom number generators are
important in practice for their speed in number generation and their
reproducibility.
This particular implementation is using a Linear congruential generator.
I have a function that should initialize the values of matrix in a random way, given a probability (in this case p(0)=1/3, p(1) = 2/3 )
The problem is that the matrix always comes out to be the same:
void init_matrix(short unsigned* m,int* n_gen)
{
*n_gen=0;
int i;
for(i=0;i<N_X*N_Y;i++) {
if( ( ( rand() % 100 ) % 3) == 0 )
m[i]=0;
else
m[i]=1;
}
}
is it because of the implementation of the rand() function? Or am I using it incorrectly? Thanks!
You should call srand function to initialize random number generator:
srand(time(NULL));
init_matrix(m, &n_gen); // call your function
For every value passed as argument rand() generates a different succession.
You need to call srand in the initial section of your code in order to initialize your random number generator, as the document shows this is typical ways of calling srand:
srand(time(0));
as the document explains:
If rand() is used before any calls to srand(), rand() behaves as if it was seeded with srand(1). Each time rand() is seeded with srand(), it must produce the same sequence of values.
and this part explains why we use time(0):
Standard practice is to use the result of a call to time(0) as the seed. However, time() returns a time_t value, and time_t is not guaranteed to be an integral type. In practice, though, every major implementation defines time_t to be an integral type, and this is also what POSIX requires.