Reputation: 325
For clarification, if I had a list of 8 elements, i would want to randomly pick 2. If I had a list of 20 elements, I would want to randomly pick 5. I would also like to assure (though not needed) that two elements don't touch, i.e. if possible not the 3 and then 4 element. Rather, 3 and 5 would be nicer.
Upvotes: 0
Views: 207
Reputation: 57600
The simplest solution:
Example implementation:
use List::Util qw/shuffle/;
my @nums = 1..20;
my @pick = (shuffle @nums)[0 .. 0.25 * $#nums];
say "@pick";
Example output: 10 2 18 3 19
.
Your additional restriction “no neighboring numbers” actually makes this less random, and should be avoided if you want actual randomness. To avoid that two neighboring elements are included in the output, I would iteratively splice unwanted elements out of the list:
my @nums = 1..20;
my $size = 0.25 * @nums;
my @pick;
while (@pick < $size) {
my $i = int rand @nums;
push @pick, my $num = $nums[$i];
# check and remove neighbours
my $len = 1;
$len++ if $i < $#nums and $num + 1 == $nums[$i + 1];
$len++, $i-- if 0 < $i and $num - 1 == $nums[$i - 1];
splice @nums, $i, $len;
}
say "@pick";
Upvotes: 3
Reputation: 97938
use strict;
use warnings;
sub randsel {
my ($fact, $i, @r) = (1.0, 0);
while (@r * 4 < @_) {
if (not grep { $_ == $i } @r) {
$fact = 1.0;
# make $fact = 0.0 if you really don't want
# consecutive elements
$fact = 0.1 if grep { abs($i - $_) == 1 } @r;
push(@r, $i) if (rand() < 0.25 * $fact);
}
$i = ($i + 1) % @_;
}
return map { $_[$_] } sort { $a <=> $b } @r;
}
my @l;
$l[$_] = $_ for (0..19);
print join(" ", randsel(@l)), "\n";
Upvotes: 0