Jump to content


- - - - -

Distribution of random


12 replies to this topic

#1 AndyP

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 10 May 2006 - 10:54 AM

Hi,

Here's some code to generate a random number in the range [min,max)

int RandomInRange( const int min, const int max )

{

   return ( rand( ) % ( max - min ) ) + min;

}


But... this does not give a nice distribution. If (max - min) is not a divisor of the maximum value that rand( ) can create, then the first N values in the range will have a higher probability of being generated (where N is (rand_max + 1) % (max - min)).

e.g. if rand() generates values from 0-5 (rand_max = 5), and by some chance, it generates the sequence

0,1,2,3,4,5,0,1,2,3,4,5

This is a nice uniform distribution, but putting it through RandomInRange(0,5) gives
0,1,2,3,4,0,0,1,2,3,4,0
, which biases 0. RandomInRange(0,4) gives
0,1,2,3,0,1,0,1,2,3,0,1
which biases 0 and 1.

We can fix RandomInRange() by normalising rand():

float NormalisedRandom( )

{

   return float( rand( ) ) / float ( RAND_MAX );

}

int RandomInRange( const int min, const int max )

{

   return int( ( NormalisedRandom( ) * float( max - min ) ) + float( min ) );

}


My question is: Is there a faster, non-floating-point way of ensuring a uniform distribution in an arbitrary range?

#2 Nick

    Senior Member

  • Members
  • PipPipPipPip
  • 1225 posts

Posted 10 May 2006 - 12:21 PM

This might do the job:

short random(short min, short max)

{

    return (rand() * (max - min) + RAND_MAX / 2) / RAND_MAX + min;

}

To make it even faster, replace the division by RAND_MAX with a shift right by 15.

#3 monjardin

    Senior Member

  • Members
  • PipPipPipPip
  • 1033 posts

Posted 10 May 2006 - 12:21 PM

Don't use rand()? boost has some useful RNG tools in the random library, but I don't know about the performance.

Also, there was a discussion of random numbers a few months ago in this thread.
monjardin's JwN Meter (1,2,3,4,5,6):
|----|----|----|----|----|----|----|----|----|----|
*

#4 juhnu

    Valued Member

  • Members
  • PipPipPip
  • 292 posts

Posted 10 May 2006 - 12:56 PM

Mersenne-Twister has a good performance and random distribution: http://www.bedaux.net/mtrand/

#5 AndyP

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 10 May 2006 - 02:17 PM

juhnu: rand() generates reasonable white-noise; I'm happy with that. The problem is that, no matter what random function I use as my input into RandomInRange(), I'll get a non-uniform distribution because of the modulus.

monjardin: Thanks for the reference! boost is always full of surprises :happy: uniform_int looks promising. The docs indicate that it has the same problem as RandomInRange() - it says they ignore errors caused by the folding of values by the modulus function because if the RNG range is considerably greater than the output range, the error is minimised.

However, looking at the implementation of uniform_int, it appears to simply generate numbers using the base RNG until one falls in the correct range... (of course, this is after only 5 minutes looking at boost code - it takes at least 5 years to fully understand a boost header file, and afterwards you are either blind or insane - so I could be mistaken).

Nick: That looks good I'll give it a go :happy:

#6 .oisyn

    DevMaster Staff

  • Moderators
  • 1810 posts

Posted 10 May 2006 - 02:26 PM

Nick's code is pointless, the number of possible outcomes of rand(), N, is finite and it'll therefore always suffer from uneven distributions if the range is not a divisor of N, no matter how you do the distribution.

The only way around it is to dump the extra values, or use an alternative function that guarantees an even distribution on a given range.

(BTW Nick, RAND_MAX isn't necessarily defined as 1<<15, that's just an MSVC++ thing :wub:. Plus the fact that it's actually (1<<15)-1, so shifting isn't the same)
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#7 bramz

    Valued Member

  • Members
  • PipPipPip
  • 189 posts

Posted 10 May 2006 - 02:30 PM

If you're happy to go with rand(), then you're happy to go with biased distributions in ranges ...

You don't even want to know how many papers that examine statistical numerical experiments are actually examining the statistic properties of rand().

If you're really serious about it, don't go with rand() ...
Mersenne Twister *cough* =)
hi, i'm a signature viruz, plz set me as your signature and help me spread :)
Bramz' warehouse | LiAR isn't a raytracer

#8 AndyP

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 10 May 2006 - 02:41 PM

OK, that other thread said it all :blink:

Blaxill suggested the same normalising method that I am using, .oisyn beat it with a stick, and suggested the method that boost uses (but documents incorrectly). Looks like that might be the best answer...

I guess one way to improve on it is to do something like:

int RandomInRange( const int min, const int max )

{

   const int range = max - min;


   // Find a divisor for RAND_MAX that is greater than (max-min)

   const int mod = FindFirstDivisor(RAND_MAX,range);


   int result = range;

   while ( result >= range )

   {

      result = rand( ) % mod;

   }


   return result + min;

}

which'll cut down on the number of iterations you have to do, especially for small output ranges.

Hey ho.

#9 AndyP

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 10 May 2006 - 02:48 PM

GAH! Smart people post while I am busy formulating my reply :angry:

bramz: Mersenne twister it is.

.oisyn: See my last post, where the light slowly dawns.

#10 Nick

    Senior Member

  • Members
  • PipPipPipPip
  • 1225 posts

Posted 10 May 2006 - 07:14 PM

Oh, I thought the focus was on making it fast and 'almost' correct, for small ranges. That's too many wrong assumptions for one topic... My bad! :blush:

#11 kusma

    Valued Member

  • Members
  • PipPipPip
  • 163 posts

Posted 16 May 2006 - 12:10 AM

fixed point ;)

long long NormalisedRandom2( )
{
	return (long long)rand() * ((1UL << 31) / RAND_MAX);
}

int RandomInRange2( const int min, const int max )
{
	return (int)((NormalisedRandom2() * (max - min)) >> 31) + min;
}


#12 .oisyn

    DevMaster Staff

  • Moderators
  • 1810 posts

Posted 16 May 2006 - 12:35 AM

And how does this help AndyP's problem?
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#13 kusma

    Valued Member

  • Members
  • PipPipPip
  • 163 posts

Posted 16 May 2006 - 12:38 AM

...because it is a faster, non-floating point version of the fix he provided himself? (the initial question, that is)

edit: i was under the impression that he was already pleased with the floating point method





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users