Re: randomly choosing elements from an array
by jdporter (Paladin) on Mar 19, 2003 at 06:44 UTC
|
The canonical way to choose one element randomly from an array @a is
$item = $a[ rand @a ];
If you need to do this n times, you could therefore do this:
my @items;
for ( 1 .. $n )
{
push @items, $a[ rand @a ];
}
However, you have not said whether you can validly choose the same value twice.
If not, then you'll have to remove each element from the set when you choose it.
Like so:
my @items;
for ( 1 .. $n )
{
push @items, splice @a, rand @a, 1;
}
Of course, if you'd prefer not to destroy @a, then work on a disposable copy of it.
jdporter The 6th Rule of Perl Club is -- There is no Rule #6. | [reply] [d/l] [select] |
|
I recently had to solve the 'randomly select n unique elements from an array' problem. The solution I hit upon was to shuffle the list and take the first n elements. The trick being, you don't have to shuffle the whole list, you just need to ensure the first n elements were shuffled:
my @list = 'a'..'z'; # use your data in
+stead of this
my $n = 5; # how many do you
+need?
foreach my $i (0..$n-1) {
my $j = rand @list;
($list[$i], $list[$j]) = ($list[$j], $list[$i]); # swap ith and jth
+ elements
};
print join(', ', @list[0..$n-1]), "\n";
As you say though, the original poster didn't specify whether unique selections were a requirement. | [reply] [d/l] |
|
Shuffling is still more work than you need to do if you can be destructive to the list. To pick $n items from list @list, use:
my @result;
push @result, splice @list, rand @list, 1 while @result < $n;
-- Randal L. Schwartz, Perl hacker
Be sure to read my standard disclaimer if this is a reply. | [reply] [d/l] |
|
| [reply] |
|
This would seem to disallow duplicate choices:
my @items;
while (1) {
my $item = $a[ rand @a ];
push @items, $item unless $item ~~ @items;
last if $n <= @items;
}
| [reply] [d/l] |
Re: randomly choosing elements from an array
by dws (Chancellor) on Mar 19, 2003 at 06:01 UTC
|
From this I want to randomly pick out an x number (in this case let's say we want to pull back 2) words from the list.
Introduce a level of indirection. Instead of picking from the list, pick from a set of indices into the list. Randomize the set of indices, and pick the first X.
| [reply] |
|
| [reply] |
|
What dws mean is:
- create an index array containing all valid index. For example, if you have a list of 5 words, then this index array world be (0,1,2,3,4).
- Shuffle this inddex array randomly, so it becomes something like (3,0,4,2,1). (I guess, the difficulty to you would be how to shuffle this array ;-)
- Now pick the words. For example, continue from step 2, say you want pick 2 words. As the first two elements of the shuffled index array is 3 and 0, so you just pick the words at index 3 and 0 of your word list.
| [reply] |
|
|
| [reply] |
Re: randomly choosing elements from an array
by domm (Chaplain) on Mar 19, 2003 at 08:59 UTC
|
--
#!/usr/bin/perl
for(ref bless{},just'another'perl'hacker){s-:+-$"-g&&print$_.$/}
| [reply] [d/l] [select] |
Re: randomly choosing elements from an array
by pg (Canon) on Mar 19, 2003 at 06:12 UTC
|
There are different ways to do this.
One way is to have another array to remember which word has already been used (logically this is a bit mask), so you would not pick a used word twice (assume this is one of your requirement).
- For the first word, you just generate a random number within the range of array index, and pick the word at that index. Also mark it as used.
- Start from the second word, you generate a random number r first, if the word at index r is not used, pick it, and mark as used; if it is already used, check whether r + 1 is used, this goes on and on, until you find an unused one. Imaging the array as a ring, when you reach the end, go back to the beginning.
| [reply] |
Re: randomly choosing elements from an array
by zby (Vicar) on Mar 19, 2003 at 09:12 UTC
|
| [reply] |
Re: randomly choosing elements from an array
by Segfault (Scribe) on Mar 19, 2003 at 06:05 UTC
|
Perhaps something like this?
my @array = qw(aaron george bob tom nick heyyou john);
my $numitems = int(rand($#array));
for(my $i = 0; $i < $numitems; $i++) {
my $item = int(rand($#array));
print @array[$item] . "\n";
}
| [reply] [d/l] |
|
The proper end index is int rand @array, not int rand $#array. For example, for an array of 7 items, rand @array would return a number that is equal to or higher than 0 or, and below 7. That means that the largest number you can ever get would be like 6.9999999999.... int truncates this to an integer towards zero (AKA "rounding down"), so the largest integer you can ever get this way, is 6.
What's more, rand is uniformely distributed, which means that the chance of getting an outcome within any subrange is proportional to the size of that subrange, thus, the difference between upper and lower limit of the subrange — the lower edge is included in this range, the upper edge is not. Therefore, after int, the chance of getting any integer in the range is the same for all, as the size of each subrange resulting in one particular integer value is 1 — including the last one.
| [reply] [d/l] [select] |