tags:

views:

370

answers:

3

This is a follow on from a previously posted question:

http://stackoverflow.com/questions/822323/how-to-generate-a-random-number-in-c

I wish to be able to generate a random number from within a particular range, such as 1 to 6 to mimic the sides of a dice.

How would I go about doing this? I'm used to C# and it seems to be a lot more difficult in C to do this.

Thanks

+1  A: 
#include <time.h>
#include <stdlib.h>

srand(time(NULL));
int N = 6;
int r = rand() % N;

Generates a number in the range 0 .. N - 1 inclusive. If you want the range to start with a number above zero, just shift it up by adding a constant.

For example:

int r = M + rand() % N;

Generates a random number in the range M .. M + N - 1 inclusive

Eli Bendersky
Note that unless N is a power of 2, this will skew the distribution in favor of lower values.
Tal Pressman
+2  A: 

Wouldn't you just do:

srand(time(NULL));
int r = ( rand() % 6 ) + 1;

% is the modulus operator. Essentially it will just divide by 6 and return the remainder... from 0 - 5

Atømix
It will give results from 1 - 6. That's what the + 1 is for.
Atømix
Caught between page refreshes. I was updating while you were writing. ;-)
Atømix
Simon, show me a libc in use anywhere where `rand()` includes the low-order bits of the generator's state (if it uses an LCG). I haven't seen one so far—all of them (yes, including MSVC with RAND_MAX being just 32767) *remove* the low-order bits. Using modulus isn't recommended for other reasons, namely that it skews the distribution in favor of smaller numbers.
Joey
@Johannes: So it's safe to say slot machines don't use modulus?
Atømix
How would I exclude a 0? It seems that if I run it in a loop of 30, maybe the second or third time it runs there's a 0 roughly half way into it. Is this some sort of fluke?
Jamie Keeling
@Johannes: Maybe it's not so much an issue nowadays, but traditionally using the low-order bits isn't advisable. http://c-faq.com/lib/randrange.html
jamesdlin
@ato: yes, pretty much. Although there's a neat way around of it; Just detect when your generated number falls into the incomplete interval that actually skews the distribution and reject it in that case. That's what Java does and they have a pretty nice implementation of it.
Joey
@James: For LCG and *some* other generators I agree. However, this should only ever be an issue if you're implementing the PRNG yourself because every library and framework I've seen so far shields you from that. Note however, that there are also generators that yield low-quality *high-order* bits. Since C doesn't even specify what PRNG to use, blindly recommending throwing away low-order bits doesn't do much good.
Joey
+4  A: 
unsigned int
randr(unsigned int min, unsigned int max)
{
       double scaled = (double)rand()/RAND_MAX;

       return (max - min +1)*scaled + min;
}

See here for other options.

nos
+1: Slightly more random than the obvious solution using `%`.
S.Lott
But you're adding overhead for what's a very simple operation. And C is all about efficiency and performance. Otherwise, why use it? ;-)
Atømix
@S.Lott - not really. Each distributes the slightly-higher-odds cases differently, that's all. The double math gives the impression that there's more precision there, but you could just as easily use `(((max-min+1)*rand())/RAND_MAX)+min` and get probably the exact same distribution (assuming that RAND_MAX is small enough relative to int to not overflow).
Steve314
This is slightly dangerous: it's possible for this to (very rarely) return `max + 1`, if either `rand() == RAND_MAX`, or `rand()` is very close to `RAND_MAX` and floating-point errors push the final result past `max + 1`. To be safe, you should check that the result is within range before returning it.
Mark Dickinson
Come on guys... dice doesn't need to be that random. Otherwise, how you emulate the "cheating" factor of trying to roll double sixes in monopoly. ;-)
Atømix
`scaled` whould be a random value within `[0;1[`, which means you should divide by `RAND_MAX + 1.0`; this'll prevent the return value `max + 1` and converts `RAND_MAX` to `double` to prevent an overflow when `RAND_MAX == INT_MAX`
Christoph
@Christoph: I agree about `RAND_MAX + 1.0`. I'm still not sure that's good enough to prevent a `max + 1` return, though: in particular, the `+ min` at the end involves a round that could end up producing `max + 1` for large values of rand().Safer to abandon this approach altogether and use integer arithmetic.
Mark Dickinson
Mark Dickinson
Grr. Replace "any positive double" by "any finite positive double that's strictly larger than `DBL_MIN`" in the previous comment.
Mark Dickinson
It's not a precision issue. It's a random issue. Read Knuth's analysis of linear congruential random number generators. The left-most bits are more random than the right most bits.
S.Lott