Re: math::trulyrandom

From: Dave (dave_at_nospam.com)
Date: 07/26/04


Date: Sun, 25 Jul 2004 23:15:17 -0400

Sisyphus wrote:
> Daniel Miller wrote:
>
>> Hello,
>>
>> I'm still trying to figure out modules. I need to get 10,000 random
>> numbers
>> between 1 and 5936277172. I've been using the rand function but it seems
>> that the significant digits aren't large enough to account for these
>> large
>> numbers so if I ask for say 1000 random numbers in this range I get 10
>> duplicates! The number of duplicates goes up exponentially as I generate
>> more randoms. Anyway, I'd like to use Math::trulyrandom to generate true
>> random numbers in this range but I need some help with the syntax. Can
>> someone give me a short snippet of code to show how I would generate a
>> random number using this module?
>>
>
> This module doesn't work for me, and hasn't been updated for over 8
> years. I think you would be better advised to use one of the "quality"
> random number generator modules on cpan - say Crypt::Random.
>
> 'perl -V:randbits' will tell you how many random bits rand() produces.
> You'll probably find that you're getting only 15 random bits (even
> though '5936277172' is a 31 bit number) and that's why you find an
> unexpectedly high number of duplicates.
>
> Just for the fun of it, here's a script that produces 1000 random
> numbers in the required range (without duplicates) using only perl's
> rand function. It does this by selecting 31 random bits at each iteration.
>
> -------------------------------------
> use warnings;
> use strict;
>
> my @r;
> my $reps = 1000;
> my $max = 5936277172;
>
> # Fill @r with $reps random uniform numbers less than $max
> for(1..$reps) {push @r, create_rand($max)}
>
> # Find out how many duplicates @r contains
> print count_dup(\@r), "\n";
>
> sub create_rand {
> my $iterations = length(sprintf("%b", $_[0]));
> my $ret = '';
> for(1..$iterations) {$ret .= int(rand(2))}
> $ret = oct("0b$ret");
> $ret = int($ret / (2 ** $iterations) * $_[0]);
> return $ret;
> }
>
> sub count_dup {
> my $ret = 0;
> for(my $i = 0; $i < @{$_[0]}; $i++) {
> for(my $j = $i + 1; $j < @{$_[0]}; $j++) {
> if(${$_[0]}[$i] == ${$_[0]}[$j]) {$ret++}
> }
> }
> return $ret;
> }

Here is a *much* faster (and more accurate) way of finding the duplicates:

sub count_dup {
my %dupes;
my $ret = 0;
for (@{$_[0]}) {
$ret++ if $dupes{$_}++;
}
return $ret;
}

Dave

>
> __END__
> -------------------------------------------
>
> Cheers,
> Rob
>
>



Relevant Pages