Just for the record, since everyone's talking about it but noone has shown any code:
use List::Util qw( shuffle );
sub distinct_random_int {
my ( $num, $start, $end ) = @_;
return if $start > $end or $num > $end - $start + 1;
( shuffle $start .. $end )[ 0 .. $num - 1 ];
}
Update: indeed, so long as the size of the range and the number of elements requested is within an order of magnitude, this is faster than the hash&retry solutions, in my tests by about 20%. If the discrepancy grows any larger, the hash&retry approach becomes much cheaper very quickly.
use strict;
use warnings;
use Benchmark qw( cmpthese );
use List::Util qw( shuffle );
sub randseq_shuffle {
my ( $num, $start, $end ) = @_;
return if $start > $end or $num > $end - $start + 1;
( shuffle $start .. $end )[ 0 .. $num - 1 ];
}
sub randseq_retry {
my ( $num, $start, $end ) = @_;
return if $start > $end or $num > $end - $start + 1;
my %seen;
my $range = $end - $start;
my $base = $start;
undef $seen{ $base + int rand $range } until $num == keys %seen;
keys %seen;
}
for (
[ 5, 10, 30 ],
[ 50, 100, 300 ],
[ 500, 1000, 3000 ],
[ 5000, 10000, 30000 ],
[ 50000, 100000, 300000 ],
[ 5000, 10000, 30000 ],
[ 500, 10000, 30000 ],
[ 50, 10000, 30000 ],
[ 5, 10000, 30000 ],
) {
my ( $n, $s, $e ) = @$_;
cmpthese -1 => {
"shuffle($n,$s,$e)" => sub { randseq_shuffle $n, $s, $e },
"retry($n,$s,$e)" => sub { randseq_retry $n, $s, $e },
};
}
Makeshifts last the longest.
|