Re: Shock: rand function is not uniform!
[email protected] (James Taylor) Mon, 18 Feb 2002 17:38:52 +0000 (GMT)
| Newsgroups | perl.riscos |
|---|---|
| Message-ID | <[email protected]> |
On Mon 18 Feb, Richard Proctor wrote:
>
> The rand function in the current riscos perl is based on the C
> function "rand", which is well known to be less the ideal.
Less than ideal? That's rather an understatement. Did you run
the program I attached which plots a graph of its distribution?
From the graph it is clear that the probability of getting a
value in the middle of rand's range is around about a *quarter*
of the probability of getting values at the two extremes of
its range. Now I don't think you could describe that as
anything less than an unmitigated disaster!
> if/when we move riscos perl to the more recent version which
> uses the C fucntion rand48 which is more random.
I really don't think the problem with Perl's rand has anything
to do with any minor randomness failings in C's rand. I have
written an equivalent test program in C and it is clear from
the graph that whatever failings C's rand might have, it is
at least approximately uniform. The same goes for C's
_ANSI_rand() function which is supposed to be even less
good. I have also tested Perl's rand function on various
Unix boxes and found that it behaves properly. In comparison,
RISC OS Perl's rand function is *so* broken that I can only
think that there's a bug in the way it was implemented.
[Re: alternative random function]
> Go ahead post it.
Okay, thanks, here you go:
{
my @q; # Holds random number queue
# init_random() parameters:
# Either a number to use as a seed
# or none to seed on PID and time
sub init_random (;$) {
my $seed = shift;
if (defined $seed) {
srand $seed;
} else {
srand(($$ + ($$ << 15)) ^ time);
}
@q = ();
push @q, int rand(1 << 31) while @q < 55;
}
# random() parameters:
# Either a multiplier $mul for an int between 0<=x<$mul
# or none for a float between 0<=x<1,
# See Knuth vol 2 sec 3.2.2 eq 7 for the algorithm used
sub random (;$) {
my $mul = shift;
init_random unless @q;
my $next = ($q[-24] + $q[-55]) % (1 << 31);
push @q, $next;
shift @q;
if (defined $mul) {
return int $next / (1 << 31) * $mul;
} else {
return $next / (1 << 31);
}
}
}
Hope that's helpful to someone... :-)
--
James Taylor <[email protected]>
Based in Southam, Cheltenham, UK.
PGP key available ID: 3FBE1BF9
Fingerprint: F19D803624ED6FE8 370045159F66FD02