Wednesday, October 15, 2008

Random Thoughts: rand() vs. arc4random()

There are several built-in randomizers on the iPhone, and most people's first thought is to use rand() after seeding it by calling

srandom(time(NULL));

But... rand() is really not a very good PRNG. random() is a little better, but still less then ideal. Fortunately, these are not the only ones available on the iPhone. Personally, I like arc4random() because it's a decent pseudo-random algorithm and has twice the range or rand().

On the iPhone, RAND_MAX is 0x7fffffff (2147483647), while arc4random() will return a maximum value of 0x100000000 (4294967296), giving much more precision. You also don't need to seed arc4random(), as the first call to it automatically seeds it.

To get an integer value from arc4random() that goes from 0 to x-1, you would do this:

int value = arc4random() % x;

To get an integer in the range 1 to x, just add 1:

int value = (arc4random() % x) + 1;

The parenthesis aren't really necessary based on order of operation rules, but I'm anal about parens.

Finally, if you need to generate a floating point number, define this in your project:

#define ARC4RANDOM_MAX 0x100000000

Then, you can use arc4random() to get a floating point value (at double the precision of using rand()), between 0 and 100, like so:

double val = floorf(((double)arc4random() / ARC4RANDOM_MAX) * 100.0f);




20 comments:

pfhorte said...

You talk about better randomness, but using % is NOT more random.

Lets say random returned a number from 0 - 10 and I wanted one from 0 - 9. I would use % 10. But every time random returned 10, it would get converted to 0, thus doubling all the zeroes returned.

Scaling is better than mod unless performance is a concern.

Jeff LaMarche said...

If the range of the random number actually was from 1 to 10, or even 1 to 100,000, I could see your point, however arc4random() returns a value from 1 to 4294967296. The skew you would get from using modulus math on the resulting value could not be more than x/4294967296, and that's a worst case scenario if 4294967296 is equally divisible by x. We're talking about a number so infinitesimally small that it can't be displayed on most calculators except by using scientific notation. A value that wouldn't register as skew in the vast, vast majority of practical applications.

Jeff LaMarche said...

Whoops, slight error in that last post (I can't do math when I've been drinking :) - when x is evenly divisible by 4294967296, that's actually the best case scenario, it's when x-1 is evenly divisible by 4294967296 that we see worst case scenario. Doesn't change the basic point, though, that modulus math will not skew the results enough for most purposes. If you're doing mission critical simulations, then by all means, take the performance hit, but otherwise, modulus is fine.

BackSlash said...
This comment has been removed by the author.
Allen Jordan said...

Thanks for the tip Jeff. After reading this, I've been using arc4random in my iPhone game with good success. Your Beginning iPhone Development book has helped me out a lot too (I have a copy sitting next to me right now).

Michelle said...

Hope I'm not too late to the ball...

If one is truly concerned about skewing when the maximum value is returned by a PRNG, you can always put in a check to discard that value and get a new random number before you do the mod operation.

To know whether this is faster than the Scaling alternative that pfhorte suggests, I'd need to know what he is proposing and how it would be implemented.

Jaimi said...

arc4random returns a 32 bit integer - so the most it could possibly return is 0xffffffff. There's no way it could return
0x100000000.

SWI said...

He type-casted before division.. so it is actually double, not uint32_t.

Arafat said...

Thanks for the syntax :D. Now I can make my character move randomly :D

Stephen said...

For anybody who is looking for a way to get a random number between a range of numbers (say 3-12), this is how I did it:

int min = 3;
int max = 12;
int numberOfSides = (arc4random() % (max - min)) + min;
NSLog(@"Random is %d", numberOfSides);

This will give you a random number within a range of 9 numbers and then offsets it from 0 to make the numbers be at least the minimum, but not more than max. I found the following method did not work for me:

int value = (arc4random() % x) + 1;

It did not work because it makes the random numbers exceed the maximum value. It adds one to the max value. So, you have to first subtract that value and then add it as an offset. The original post was very helpful though.

Register said...

How would you generate a range of random values *INCLUDE* negative values?

From -10 to +20

Taber said...

Register: You could do something like this:

-(NSInteger) getRandFrom: (NSInteger) min to: (NSInteger) max
{
return (arc4random() % (max - min)) + min;
}

[self getRandFrom: -10 to: 20];

That should do the trick.

Colin said...
This comment has been removed by the author.
Colin said...

If anyone else gets a problem compiling this try adding LL to the end of #define ARC4RANDOM_MAX 0x100000000.

So...

#define ARC4RANDOM_MAX 0x100000000LL

hope this is correct and helps someone.

Thanks for the function.

Lucas said...

How can I make that with texts? Like every time the user changes the page, the app would give a random quote

Edwin said...

scrub m65 kamagra attorney lawyer body scrub field jacket lovegra marijuana attorney injury lawyer

Ryan said...

I was using arc4random in a game and I had a particular scenario where I wanted to do something random based on 3 weights: 0.4, 0.4, 0.2 (adding up to 1.0). My code was something like this:

r = (double)arc4random() / (double)UINT_MAX;

if (r <= 0.4) { ... }
else if (r <= 0.8) { ... }
else { ... }

What I found is that the first and third options seemed to hit more frequently than the second one. I watched the values in a debugger for a while to make sure my math was right--I never got a value > 1.0 and I did get values in the 0.4 to 0.8 range from time to time, but nowhere near as often as < 0.4 and > 0.8. I switched to rand() (true randomness is really not critical in this case) and the distribution seems a lot more even.

Has anyone seen this? Is this a known issue?

h4ns said...

What youre saying is completely true. I know that everybody must say the same thing, but I just think that you put it in a way that everyone can understand. I also love the images you put in here. They fit so well with what youre trying to say. Im sure youll reach so many people with what youve got to say.

Arsenal vs Huddersfield Town live streaming
Arsenal vs Huddersfield Town live streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Notts County vs Manchester City Live Streaming
Notts County vs Manchester City Live Streaming
Bologna vs AS Roma Live Streaming
Bologna vs AS Roma Live Streaming
Juventus vs Udinese Live Streaming
Juventus vs Udinese Live Streaming
Napoli vs Sampdoria Live Streaming
Napoli vs Sampdoria Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
AS Monaco vs Marseille Live Streaming
AS Monaco vs Marseille Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Technology News | News Today | Live Streaming TV Channels

launch-mailinator-com said...

"and most people's first thought is to use rand() after seeding it by calling srandom(time(NULL));"

Shows how most people are really stupid. srandom() seeds random(), while srand() seeds rand(). Using srandom() to seed rand() might not work so well

Jörg Bühmann said...

The value of ARC4RANDOM_MAX is wrong.
arc4random() returns a maximum of (2**32)-1 = 4294967295 = 0xFFFFFFFF
See http://developer.apple.com/library/ios/#documentation/System/Conceptual/ManPages_iPhoneOS/man3/arc4random.3.html

You don't have to define that yourself, just use UINT32_MAX. If you use random() use RAND_MAX.