Arian
Arian

Reputation: 325

Using perl, given an array of any size, how do I randomly pick 1/4 of the list

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

Answers (2)

amon
amon

Reputation: 57600

The simplest solution:

  1. Shuffle the list
  2. select the 1st quarter.

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

perreal
perreal

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

Related Questions