views:

249

answers:

5

So, I'm trying to create a random vector (think geometry, not an expandable array), and every time I call my random vector function I get the same x value, though y and z are different.

int main () {
    srand ( (unsigned)time(NULL));
    Vector<double> a;
    a.randvec();
    cout << a << endl;
    return 0;
}

using the function

//random Vector
template <class T>
void Vector<T>::randvec()
{
    const int min=-10, max=10;
    int randx, randy, randz;

    const int bucket_size = RAND_MAX/(max-min);

    do randx = (rand()/bucket_size)+min;
    while (randx <= min && randx >= max);
    x = randx;

    do randy = (rand()/bucket_size)+min;
    while (randy <= min && randy >= max);
    y = randy;

    do randz = (rand()/bucket_size)+min;
    while (randz <= min && randz >= max);
    z = randz;
}

For some reason, randx will consistently return 8, whereas the other numbers seem to be following the (pseudo) randomness perfectly. However, if I put the call to define, say, randy before randx, randy will always return 8.

Why is my first random number always 8? Am I seeding incorrectly?

A: 

A simple quickfix is to call rand a few times after seeding.

int main ()
{
    srand ( (unsigned)time(NULL));
    rand(); rand(); rand();

    Vector<double> a;
    a.randvec();
    cout << a << endl;
    return 0;
}

Just to explain better, the first call to rand() in four sequential runs of a test program gave the following output:

27592
27595
27598
27602

Notice how similar they are? For example, if you divide rand() by 100, you will get the same number 3 times in a row. Now take a look at the second result of rand() in four sequential runs:

11520
22268
248
10997

This looks much better, doesn't it? I really don't see any reason for the downvotes.

FredOverflow
???????????????
paxdiablo
strange that it EVER worked for you
the_drow
@pax: What is there not to understand? If the first n numbers from `rand()` are always the same, you can simply discard those first n numbers by calling `rand()` n times and ignoring the result.
FredOverflow
@Nick: Could you test this please and post if it works for you?
FredOverflow
This won't help at all. It will just shift the random number sequence three to the left.
Peter Alexander
@FredOverflow This works, but it's a bit of a hack and doesn't really figure out what's going on – it just deals with it. If I can't figure this problem out, I'll use your technique, but I'd rather get to the heart of the issue. Thanks, though!
Nick Sweet
@Nick: I have updated my post, maybe this helps.
FredOverflow
It is a bit woodoo programming but the advice doesn't seem that bad. If the first value you get is too similar but the rest are OK, then throw away the first value(s). The idea of rand is that completely different sequences emerge from seeds that are rather similar.
visitor
+2  A: 

Also to mention, you can even get rid of that strange bucket_size variable and use the following method to generate numbers from a to b inclusively:

srand ((unsigned)time(NULL));

const int a = -1;
const int b = 1;

int x = rand() % ((b - a) + 1) + a;
int y = rand() % ((b - a) + 1) + a;
int z = rand() % ((b - a) + 1) + a;
Kotti
+7  A: 

I don't see any problem with your srand(), and when I tried running extremely similar code, I did not repeatedly get the same number with the first rand(). However, I did notice another possible issue.

do randx = (rand()/bucket_size)+min;
while (randx <= min && randx >= max);

This line probably does not do what you intended. As long as min < max (and it always should be), it's impossible for randx to be both less than or equal to min and greater than or equal to max. Plus, you don't need to loop at all. Instead, you can get a value in between min and max using:

randx = rand() % (max - min) + min;
Justin Ardini
While this is true, it doesn't answer the question at all.
Job
So, it is a one-cycle 'do while' loop - unnecessary but harmless.
Jonathan Leffler
Harmless except that it doesn't do what it's expected to -- and someone glancing over the code (and not looking too closely) will see it checking bounds, when it actually isn't.
cHao
@Job: Good point, just edited my answer.
Justin Ardini
Your formula is wrong: it should be `randx = rand() % (max - min) + min`.
Matthieu M.
I have to say, I'm a little confused – I took the do while loop straight out of Accelerated C++ by Andrew Koenig and Barbara E. Moo. They give this reason: rand() really returns only pseudo-random numbers. Many C++ implementations' pseudo-random-number generators give remainders that aren't very random when the quotients are small integers. For example, it is not uncommon for successive results of rand() to be alternately even and odd. In that case, if n is 2, successive results of rand() % n will alternate between 0 and 1.I tried the modulo version, and now '0' become my x value each time.
Nick Sweet
This is a bit of a hack, but if I add a line int temp = rand();my problems go away!
Nick Sweet
Justin Ardini
Nick Sweet
+5  A: 

The issue is that the random number generator is being seeded with a values that are very close together - each run of the program only changes the return value of time() by a small amount - maybe 1 second, maybe even none! The rather poor standard random number generator then uses these similar seed values to generate apparently identical initial random numbers. Basically, you need a better initial seed generator than time() and a better random number generator than rand().

The actual looping algorithm used is I think lifted from Accelerated C++ and is intended to produce a better spread of numbers over the required range than say using the mod operator would. But it can't compensate for always being (effectively) given the same seed.

anon
You're right – it's straight from Accelerated C++.However, I'm not sure you're right about the seeded values being that close together. I'm running the program many times in the same minute (say 10 times in 20 seconds) and each time I still get 8 as my first random number. The first numbers I'm getting from rand each time I run it are:204668022361002409620471340121436194148204725166185502207020473356961746421126204745334516994273482047520573198089577420476046081258989483That seems to be doing its job, right?
Nick Sweet
@Nick The numnbers are different, but they are not VERY different, which is what the algorithm from Accelerated C++ requires. It seems unlikely that if you choose 10 genuinely random numbers they will all begin with the digits "20".
anon
+1  A: 

Your implementation, through integer division, ignores the smallest 4-5 bit of the random number. Since your RNG is seeded with the system time, the first value you get out of it will change only (on average) every 20 seconds.

This should work:

randx = (min) + (int) ((max - min) * rand() / (RAND_MAX + 1.0));

where

rand() / (RAND_MAX + 1.0)

is a random double value in [0, 1) and the rest is just shifting it around.

Anonymous Coward
I think you correct, but when I tried your code it produced similar problems to the OP's. If I remove the cast to `int` I see variation in the 2nd decimal place. I assume that if I waited long enough between runs, I'd see `randx` change, but that could be years at this rate.
Whisty
Nope – the same number comes up for the first value to which I assign a random number each time I run the program.
Nick Sweet