0

I am new to objective C and trying to understand arc4random().

There are so many conflicting explanations on the web. Please clear my confusion, which of the following is correct:

// 1.
 arc4random() % (toNumber - fromNumber) + fromNumber;

OR

//2.
arc4random() % ((toNumber - fromNumber) + 1) + fromNumber; 

//toNumber-fromNumbers are any range of numbers like random # between 7-90.
madhead
  • 31,729
  • 16
  • 153
  • 201
  • 2
    Do you want toNumber to be possible value or it should be excluded? – Vladimir Mar 11 '13 at 16:29
  • Your question could be more clear. Do you have a question about the function return value or do you have a question about what the module operator accomplishes? – WeakPointer Mar 11 '13 at 19:38

2 Answers2

6

This code will get you a random number between 7 and 90.

NSUInteger random = 7 + arc4random_uniform(90 - 7);

Use arc4random_uniform to avoid modulo bias.

Adam
  • 26,549
  • 8
  • 62
  • 79
4

Adam's answer is correct. However, just to clarify the difference between the two, the second one raises the possible range by one to make the range inclusive. The important thing to remember is that modulo is remainder division, so while there are toNumber possible outcomes, one of them is zero (if the result of arc4random() is a multiple of toNumber) and toNumber itself can not be the remainder.

// 1.
 arc4random() % (10 - 5) + 5;

This results in a range of 0 + 5 to 4 + 5, which is 5 to 9.

//2.
arc4random() % ((10 - 5) + 1) + 5; 

This results in a range of 0 + 5 to (4 + 1) + 5, which is 5 to 10.

Neither is correct or incorrect if you wish to use modulo. One is exclusive of the upper range while the other is inclusive of the upper range. However, if you think about how remainder division works and think of the pool of numbers returned by any PRNG in terms of cycles the length of your total range, then you'll realize that if the range does not divide evenly into the maximum range of the pool you'll get biased results. For instance, if arc4random() returned a result from 1 to 5 (it doesn't, obviously) and you wanted a number from 0 to 2, and you used arc4random() % 3, these are the possible results.

1 % 3 = 1
2 % 3 = 2
3 % 3 = 0
4 % 3 = 1
5 % 3 = 2

Note that there are two ones and two twos, but only one zero. This is because our range of 3 does not evenly divide into the PRNG's range of 5. The result is that (humorously enough) PRNG range % desired range numbers at the end of the cycle need to be culled because they are "biased"–the numbers themselves aren't really biased, but it's easier to cull from the end. Failing to do this results in the lower numbers of the range becoming more likely to appear.

We can cull the numbers by calculating the upper range of the numbers we can generate, modulo it with the desired range and then pull those numbers off of the end. By "pull those numbers off of the end" I really mean "loop infinitely until we get a number that isn't one of the end numbers".

Some would say that's bad practice; you could theoretically loop forever. In practice, however, the expected number of retries is always less than one since the modulo bias is never more than half the pool (usually much less than that) of the PRNG's numbers. I once wrote a wrapper for rand using this technique.

You can see an example of this in the source for OpenBSD, where arc4random_uniform calls arc4random in a loop until a number is determined to be clean.

Metabble
  • 11,773
  • 1
  • 16
  • 29