53

I have two text boxes and user can input 2 positive integers (Using Objective-C). The goal is to return a random value between the two numbers.

I've used "man arc4random" and still can't quite wrap my head around it. I've came up with some code but it's buggy.

float lowerBound = lowerBoundNumber.text.floatValue;
float upperBound = upperBoundNumber.text.floatValue;
float rndValue;
//if lower bound is lowerbound < higherbound else switch the two around before randomizing.
if(lowerBound < upperBound)
{
    rndValue = (((float)arc4random()/0x100000000)*((upperBound-lowerBound)+lowerBound));
}
else 
{
    rndValue = (((float)arc4random()/0x100000000)*((lowerBound-upperBound)+upperBound));
}

Right now if I put in the values 0 and 3 it seems to work just fine. However if I use the numbers 10 and 15 I can still get values as low as 1.0000000 or 2.000000 for "rndValue".

Do I need to elaborate my algorithm or do I need to change the way I use arc4random?

Demasterpl
  • 2,103
  • 5
  • 24
  • 32
  • http://iphonedevelopment.blogspot.com/2008/10/random-thoughts-rand-vs-arc4random.html – Andrew Mar 13 '12 at 04:32
  • 1
    Your parentheses are wrong. Delete the open parenthesis after `*` and the close parenthesis before `;`. You still have rounding issues though. – David Schwartz Mar 13 '12 at 04:34
  • 2
    The general formula `minValue + ((maxValue - minValue) * rnd())` where `rnd()` returns a random floating-point value between 0.0 and 1.0 will return the value you're looking for. I leave it to you to convert that into Obj-C and optimize it to suit your needs. – Jim H. Mar 13 '12 at 04:34
  • @DavidSchwartz:Thanks! That actually did the trick! – Demasterpl Mar 13 '12 at 04:43
  • @JimH. Wrong formula. it needs to be `min + (max - min + 1) * rnd()`. – rmaddy Jul 17 '14 at 18:13

6 Answers6

126

You could simply use integer values like this:

int lowerBound = ...
int upperBound = ...
int rndValue = lowerBound + arc4random() % (upperBound - lowerBound);

Or if you mean you want to include float number between lowerBound and upperBound? If so please refer to this question: https://stackoverflow.com/a/4579457/1265516

Community
  • 1
  • 1
eveningsun
  • 1,322
  • 1
  • 9
  • 5
  • Sorry I misunderstand your question, please see the updated answer. – eveningsun Mar 13 '12 at 04:40
  • 2
    the code does not include the upperBound as the random output, see my answer below as the fix. – inexcii Dec 11 '13 at 07:38
  • 12
    This will generate random integers greater than or equal to `lowerBound`, but less than (but not including) `upperBound`. If you want to include `upperBound` in the range of numbers, you want `rndValue = lowerBound + arc4random() % (upperBound - lowerBound + 1)`. Or perhaps better (to eliminate modulo bias), `rndValue = lowerBound + arc4random_uniform(upperBound - lowerBound + 1)`. – Rob Jul 17 '14 at 18:13
71

The following code include the minimum AND MAXIMUM value as the random output number:

- (NSInteger)randomNumberBetween:(NSInteger)min maxNumber:(NSInteger)max
{
    return min + arc4random_uniform((uint32_t)(max - min + 1));
}

Update:
I edited the answer by replacing arc4random() % upper_bound with arc4random_uniform(upper_bound) as @rmaddy suggested.
And here is the reference of arc4random_uniform for the details.

Update2:
I updated the answer by inserting a cast to uint32_t in arc4random_uniform() as @bicycle indicated.

inexcii
  • 1,591
  • 2
  • 15
  • 28
  • 6
    It's even better to use `return arc4random_uniform(max - min + 1) + min;`. – rmaddy Jul 17 '14 at 18:11
  • @rmaddy Why is it better to just return the whole operation rather than equalling that operation to a NSInteger and then returning that NSInteger? – Supertecnoboff Aug 02 '14 at 16:35
  • 2
    @Supertecnoboff It's not. Notice the answer has been edit since my comment. The answer used to suggest using `arc4random() % (max - min + 1)`. My comment suggested using `arc4random_uniform(max - min + 1)`. – rmaddy Aug 02 '14 at 16:41
  • I really like your way of realization, elegant and simple, appreciated! – Resty Nov 20 '14 at 08:59
  • 2
    That gives the annoying implicit conversion loses integer precision warning. Better to put a cast in it: `return min + arc4random_uniform((uint32_t)(max - min + 1));` – bicycle Jan 28 '16 at 16:11
7
-(int) generateRandomNumberWithlowerBound:(int)lowerBound
                               upperBound:(int)upperBound
{
    int rndValue = lowerBound + arc4random() % (upperBound - lowerBound);
    return rndValue;
}
Atef
  • 2,872
  • 1
  • 36
  • 32
2

You should avoid clamping values with mod (%) if you can, because even if the pseudo-random number generator you're using (like arc4random) is good at providing uniformly distributed numbers in its full range, it may not provide uniformly distributed numbers within the restricted modulo range.

You also don't need to use a literal like 0x100000000 because there is a convenient constant available in stdint.h:

(float)arc4random() / UINT32_MAX

That will give you a random float in the interval [0,1]. Note that arc4random returns an integer in the interval [0, 2**32 - 1].

To move this into the interval you want, you just add your minimum value and multiply the random float by the size of your range:

lowerBound + ((float)arc4random() / UINT32_MAX) * (upperBound - lowerBound);

In the code you posted you're multiplying the random float by the whole mess (lowerBound + (upperBound - lowerBound)), which is actually just equal to upperBound. And that's why you're still getting results less than your intended lower bound.

Aaron Golden
  • 7,092
  • 1
  • 25
  • 31
2

Objective-C Function:

-(int)getRandomNumberBetween:(int)from to:(int)to
{
    return (int)from + arc4random() % (to-from+1);
}

Swift:

func getRandomNumberBetween(_ from: Int, to: Int) -> Int 
{
    return Int(from) + arc4random() % (to - from + 1)
}

Call it anywhere by:

int OTP = [self getRandomNumberBetween:10 to:99];
NSLog(@"OTP IS %ld",(long)OTP);
NSLog(@"OTP IS %@",[NSString stringWithFormat @"%ld",(long)OTP]);

For Swift:

var OTP: Int = getRandomNumberBetween(10, to: 99)
Raj Aryan
  • 363
  • 2
  • 15
0

In Swift:

let otp = Int(arc4random_uniform(6))

Try this.