22

Possible Duplicate:
Generating Random Numbers in Objective-C

How do I generate a random number which is within a range?

Community
  • 1
  • 1
TheLearner
  • 19,387
  • 35
  • 95
  • 163
  • 4
    Duplicate - f.e. http://stackoverflow.com/questions/160890/generating-random-numbers-in-objective-c – schnaader Jun 08 '10 at 16:06
  • Did you try searching at all? This has been asked a good half dozen times before. Also, all the answers below are wrong in the sense that `rand` is not a good RNG and requires seeding. You should use `arc4random` instead. – Dave DeLong Jun 08 '10 at 16:18
  • @Dave: there's no guarantee that `rand()` is necessarily bad, just no guarantee that it's good. Recommending arc4random without knowing the intended purpose makes little (if any) sense. Most beginners aren't doing anything where the quality of the generator really matters anyway. – Jerry Coffin Jun 08 '10 at 16:36
  • @Jerry agreed. However, a beginner might not know he needs to seed at all, which is one of the benefits of arc4random. There's been a lot of discussion on this topic: http://www.google.com/search?q=site:stackoverflow.com+arc4random+rand – Dave DeLong Jun 08 '10 at 16:40
  • exact duplicate: http://stackoverflow.com/questions/2509679/how-to-generate-a-random-number-from-within-a-range-c – Lucas Jun 08 '10 at 16:53
  • Dilbert's random number generator: http://stackoverflow.com/questions/84556/whats-your-favorite-programmer-cartoon/90391#90391 – Fredrik Johansson Jun 08 '10 at 17:42
  • Yeah I did do quite a bit of searching on stackoverflow and google but I found most answers were for random numbers between 0 and n. – TheLearner Jun 08 '10 at 19:42
  • It maybe a duplicate but it has one of the best answers, from Jerry – Doug Molineux Dec 28 '11 at 07:08

5 Answers5

83

This is actually a bit harder to get really correct than most people realize:

int rand_lim(int limit) {
/* return a random number between 0 and limit inclusive.
 */

    int divisor = RAND_MAX/(limit+1);
    int retval;

    do { 
        retval = rand() / divisor;
    } while (retval > limit);

    return retval;
}

Attempts that just use % (or, equivalently, /) to get the numbers in a range almost inevitably introduce skew (i.e., some numbers will be generated more often than others).

As to why using % produces skewed results: unless the range you want is a divisor of RAND_MAX, skew is inevitable. If you start with small numbers, it's pretty easy to see why. Consider taking 10 pieces of candy (that we'll assume you can't cut, break, etc. into smaller pieces) and trying to divide it evenly between three children. Clearly it can't be done--if you hand out all the candy, the closest you can get is for two kids to get three pieces of candy, and one of them getting four.

There's only one way for all the kids to get the same number of pieces of candy: make sure you don't hand out the last piece of candy at all.

To relate this to the code above, let's start by numbering the candies from 1 to 10 and the kids from 1 to 3. The initial division says since there are three kids, our divisor is three. We then pull a random candy from the bucket, look at its number and divide by three and hand it to that kid -- but if the result is greater than 3 (i.e. we've picked out candy number 10) we just don't hand it out at all -- we discard it and pick out another candy.

Of course, if you're using a modern implementation of C++ (i.e., one that supports C++11 or newer), you should usually use one the distribution classes from the standard library. The code above corresponds most closely with std::uniform_int_distribution, but the standard library also includes uniform_real_distribution as well as classes for a number of non-uniform distributions (Bernoulli, Poisson, normal, maybe a couple others I don't remember at the moment).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 5
    I'm sorry, but ewww! The last thing I want to do is loop a divide an indeterminate number (who knows, perhaps forever) of times when trying to get a random number. Skew or not, I'll take simpler or perhaps a table driven solution over this. – Michael Dorgan Jun 08 '10 at 16:17
  • 6
    @Michael: try it before yo jump to any conclusions. In fact, the "indeterminate number" is *usually* 1. Unless you do this in a *really* tight loop (e.g., filling a vector with random numbers) it's virtually impossible to even measure this being slower than the versions that are clearly wrong. – Jerry Coffin Jun 08 '10 at 16:20
  • 4
    This is technically correct but you have a GIGO problem starting with rand(). It looks like you are trying to correct the skew when limit is not a factor of RAND_MAX, using the modular division approach your error is on the order of `limit / RAND_MAX` until `limit` approaches `RAND_MAX`. The problems starting with a PRNG like Rand() dwarf effects like that. The price you pay is to introduce a non-derterministic loop with unbounded run time [IRL it runs once but we are splitting hairs here ;-)] and you gain nothing because Rand is crap to begin with. – Ukko Jun 08 '10 at 16:36
  • 3
    No, no, I get why the others are wrong and this is a good solution. But, I come from embedded land where divides, until recently, were ouch slow. Combine that with a rand() call that also tends to be on the slow side (not to mention unrandom enough that looping off them gives me a bad feeling in my stomach) and you can see where I stand. I also worried at first glance that this may not be deterministic, but it is. Bah, just gotta get used to coding in the 21st century here. – Michael Dorgan Jun 08 '10 at 16:40
  • "Attempts that just use % (or, equivalently, /) to get the numbers in a range almost inevitably introduce skew (i.e., some numbers will be generated more often than others)." That's news to me. I'm not saying it's wrong (who am I to debate with 33.7k rep), but just that factoid never came up in my own research, nor have I seen any issues based on my own casual observation. – Philip Regan Jun 08 '10 at 17:18
  • 1
    @Ukko:`rand()` may be poor. Then again, it may not be; I've used an implementation where it was the Mersenne Twister, which is pretty decent. – Jerry Coffin Jun 08 '10 at 17:18
  • 5
    @Philip Regan: The Skew comes from the little remainder after the modulus is taken. Lets say your random number generator generates 11 values 0 - 10 then you use use these number mod 5 to give you a number between 0 and 4. There are two ways to get a 3 (3 and 8) but three ways to get a 0 (0, 5, 10) so the end result is not uniform. It is a real effect, but one I argue is lost in the noise of other larger problems of using PRNGs. – Ukko Jun 08 '10 at 18:42
  • @Jerry and @Ukko: Thanks for the explanations. They make perfect sense. Looks like I have a little updating to do. Cheers! – Philip Regan Jun 08 '10 at 18:46
  • @Jerry Coffin: I would still argue that even with the MT generator this would not be the right thing, even though you have reduced the GIGO problem. But it really is a matter of what offends your coding sensibilities the most I guess. – Ukko Jun 08 '10 at 18:54
  • @Jerry on Mac OS X, rand() is poor. In particular, the bottom 12 bits go in a cycle. You should at least use random() – JeremyP Jun 09 '10 at 07:45
  • I think this code has a bug: `rand` returns a value between `[0, RAND_MAX]` *inclusive* - so when calling `rand_int` with `RAND_MAX` as its parameter, `divisor` should probably be `1`. – Thomas May 25 '21 at 10:47
  • Another problem is if you call this function with `MAX_INT` as its parameter - since this is an *inclusive* boundary it should not be a problem, but you will get an overflow when you do `limit+1`. In any event, `limit` should probably be limited to `RAND_MAX`. – Thomas May 25 '21 at 10:55
7
int rand_range(int min_n, int max_n)
{
    return rand() % (max_n - min_n + 1) + min_n;
}

For fractions:

double rand_range(double min_n, double max_n)
{
    return (double)rand()/RAND_MAX * (max_n - min_n) + min_n;
}
Michał Trybus
  • 11,526
  • 3
  • 30
  • 42
  • 6
    This will not give a uniform distribution (except for powers-of-two). – Oliver Charlesworth Jun 08 '10 at 16:17
  • 2
    I know, but it works fast, and the distribution is ok for small ranges. It is good for gamecoding. Anyway, I should have written this somewhere in the answer. – Michał Trybus Jun 08 '10 at 17:31
  • 1
    @Oli who cares? See Ukko's comment in the answer above. If you are using rand() to begin with, the teeny amount of skew (for max_n-min_n relatively small) is the least of your worries. – user168715 Jun 08 '10 at 17:37
  • 1
    @user168715: Because this approach will not map onto decent random-number implementations where this skew may well matter. – Oliver Charlesworth Jun 08 '10 at 17:46
1

For an integer value in the range [min,max):

double scale = (double) (max - min) / RAND_MAX;
int val = min + floor(rand() * scale) 
John Bode
  • 119,563
  • 19
  • 122
  • 198
-1

I wrote this specifically in Obj-C for an iPhone project:

- (int) intInRangeMinimum:(int)min andMaximum:(int)max {
    if (min > max) { return -1; }
    int adjustedMax = (max + 1) - min; // arc4random returns within the set {min, (max - 1)}
    int random = arc4random() % adjustedMax;
    int result = random + min;
    return result;
}

To use:

int newNumber = [aClass intInRangeMinimum:1 andMaximum:100]; 

Add salt to taste

Philip Regan
  • 5,005
  • 2
  • 25
  • 39
  • That solution still has the skew introduced by the modular arithmetic. Since you are using the arc4 library, it provides you with exactly the routine you want called 'arc4random_uniform' that gives properly uniform results. just change the one line to int random = arc4random_uniform(adjustedMax); – John Meacham Oct 07 '13 at 03:55
-2
+(NSInteger)randomNumberWithMin:(NSInteger)min WithMax:(NSInteger)max {
    if (min>max) {
        int tempMax=max;
        max=min;
        min=tempMax;
    }
    int randomy=arc4random() % (max-min+1);
    randomy=randomy+min;
    return randomy;
}

I use this method in a random number related class I made. Works well for my non-demanding needs, but may well be biased in some way.

Tom H
  • 1,316
  • 14
  • 26