NY Tennessean said:
(e-mail address removed) (Scott Stark) wrote in message
You've got a couple of answers suggesting you use modules that allow
you to "shuffle" an array, and I can't disagree: modules are usually
the way to go as they have been used and tested more than any home
grown solution.
But still I wanted to share with you the home-grown code I've rolled
since my original suggestion. Beware, its a little clunky. It could
stand some perfecting but "works" for me, at least for the dozen or so
times I tested it:
Yes, it does what it should, and yes, it *is* a little clunky.
sub uniq_random_int_in ($$$) {
^^^^^
Don't give a function a prototype unless there is a distinct advantage.
Normally, perl subs are not prototyped.
#based on random_int_in in perlfaq4, data manipulation
my($min, $max, $qty) = @_;
# Assumes that the three arguments are integers themselves!
# Assumes $min < $max
# Assumes $qty <= $max - $min + 1
my @set, @uniq;
This doesn't declare @uniq. "my (
@set, @uniq)" is what you need. You
didn't run this under strict, did you?
for (my $i=0; $i <= $max - $min; $i++) {
$set[$i] = $i;
}
That could be more simply written
@set = 0 .. $max - $min;
But what you really want in
@set is the set of possible results, so
you can make that
@set = $min .. $max;
for (my $i=0; $i < $qty; $i++) {
my $rand = int rand($max - $min - $i + 1);
The number "$max - $min - $i + 1" is exactly the number of elements
left in
@set in the $i-th step. You start with $max - $min + 1 elements
in the 0-th step, and you lose one element each time $i increases. So
my $rand = int rand
@set;
does the same thing. The selection process is now even clearer:
Select a random number between 0 and the number of elements left
(exclusive). Pick that element.
$uniq[$i] = $set[$rand] + $min;
When
@set contains the numbers $min .. $max directly, you don't need
the addition. $set[$rand] is the wanted random number.
@set = (@set[0..$rand-1], @set[$rand+1..scalar(@set)-1]);
^^^^^^
"
@set" is already in scalar context, no need for "scalar".
But Perl's splice() function is what you really want here:
splice(
@set, $rand, 1);
This returns the value(s) that are spliced out, so you can find
the random index and move the indicated element from
@set to @uniq
in one step:
push @uniqe, splice(
@set, rand
@set, 1);
print $uniq[$i], "\n"; #display only, can comment out
}
return @uniq;
}
@uniq_rands = uniq_random_int_in(11,20,5); # for testing
Putting it all together, this is how I would write it:
sub uniq_random_int_in {
my($min, $max, $qty) = @_;
my
@set = ( $min .. $max);
map splice(
@set, rand
@set, 1), 1 .. $qty;
}
Anno