Re: Picking unique element in random from a array
by davorg (Chancellor) on Aug 09, 2006 at 12:34 UTC
|
Shuffle the array (using "shuffle" from List::Util) and take the first ten elements.
Update: Here's a non-destructive approach that uses "shuffle":
use List::Util 'shuffle';
my @arr = (1 .. 200);
my @random = @arr[(shuffle 0 .. $#arr)[0 .. 9]];
print "@random\n";
--
< http://dave.org.uk>
"The first rule of Perl club is you do not talk about
Perl club." -- Chip Salzenberg
| [reply] [d/l] |
|
|
davorg,
Great advice and I upvoted you. It is nice to point out though that this, and randomly splicing elements of the original array do not leave it intact. Apparently Data::Random can do this as well as my suggestion of using a parallel array of indices.
Update: After looking at the source of Data::Random's rand_set(), I noticed the routine can be terribly run-time inefficient depending on the parameters. It continously selects random indices until it finds enough unique ones to satisfy the 'size' option. This is doing a lot more work than necessary. I have emailed the author with suggestions.
| [reply] |
|
|
Shuffling all 100 elements (or indices) just to get 10 of them... A partial Fisher-Yates shuffle can be more efficient. For example, using pickFromRange which I wrote after Limbic~Region asked for such a thing in the CB (probably prompted by this thread), you could do:
my @pick= @quest[ pickFromRange( 10, 0, $#quest ) ];
Though I bet it will actually be slower than just shuffling the whole list of indices for this particular case given how slow dispatching Perl opnodes is and that List::Util uses XS. But for larger lists, it could be a win.
| [reply] [d/l] |
Re: Picking unique element in random from a array
by prasadbabu (Prior) on Aug 09, 2006 at 12:27 UTC
|
use strict;
use warnings;
use Data::Random qw(:all);
my @array = (1, 2, 1..120); #assign values to array
my @ten = rand_set( set => \@array, size => 10 );#get random
print "Ten unique random numbers: @ten";
updated: Added random module. Removed identifying unique elements as per L~R's advice. As far as runtime is concerned, it is not most efficient way though. Take a look at Limbic~Region node or davorg's. Limbic~Region Thanks.
| [reply] [d/l] |
|
|
my (@index, @rand);
@index = 0 .. $#array;
push @rand, splice(@index, rand @index, 1) for 1 .. 10;
print "@array[@rand]\n";
# untested
Update: I added a couple of alternatives that do not leave the original array intact. Additionally, I need to point out that your use of Data::Random can be terribly runtime inefficient. See my update in this node for details. The alternatives consume more memory (trading space for time). I also originally incorrectly thought that the module was returning the set in the original order by default but it isn't.
| [reply] [d/l] |
Re: Picking unique element in random from a array
by Leviathan (Scribe) on Aug 09, 2006 at 12:32 UTC
|
my %seen;
for my $x (0..9)
{
my $element = $quest[rand @quest];
redo if $seen{$element}++;
print $element;
}
You set the element as a key to the hash, and you test to see if you have already used that element.
--
Leviathan | [reply] [d/l] |
|
|
Leviathan,
While this code works, it can be terribly runtime inefficient. Imagine that you have an initial data set of 10_000 elements and the desired random set is 9_990 elements. This is how Data::Random does it under the covers and I have emailed the author with a couple of alternatives.
| [reply] |
Re: Picking unique element in random from a array
by kwaping (Priest) on Aug 09, 2006 at 17:22 UTC
|
Are all the elements of your array unqiue? Also, do you mind if the original array is altered? If your answers are "yes" and "no", respectively, then this alteration to your code will work.
for my $x (0..9)
{
my $element = splice(@quest,rand @quest,1);
print $element;
}
If you don't want to alter the original array, just make a copy of it first.
---
It's all fine and dandy until someone has to look at the code.
| [reply] [d/l] |
Re: Picking unique element in random from a array
by Moron (Curate) on Aug 09, 2006 at 15:25 UTC
|
Can also sort-of explicitly sort randomly: my $limit = 0;
for $selection ( sort { rand() <=> 0.5 } @array ) {
print "$selection\n";
( ++$limit < 10 ) or last;
}
(updated to fix a minor problem). Or, if wanting to produce a new array of the results:@output = sort { rand() <=> 0.5 } @input;
$#output = 9; #truncate the randomly sorted copy
| [reply] [d/l] [select] |
Re: Picking unique element in random from a array
by pajout (Curate) on Aug 10, 2006 at 11:54 UTC
|
It should work, I hope that it does not need explanation:
#!/usr/bin/perl
my $n = 1000;
my $cnt = 10;
my %ret;
for (my $i = 0; $i < $cnt; $i++) {
my $r = int(rand()*$n);
$r++ while exists $ret{$r};
$ret{$r} = undef;
$n--;
}
print "$cnt random indexes:\n";
foreach (keys %ret) {
print "$_\n";
}
| [reply] [d/l] |
|
|
I am sorry, I'v made a mistake:
my $n = 1000;
my $cnt = 10;
my @ret;
for (my $i = 0; $i < $cnt; $i++) {
my $r = int(rand()*$n);
my $j = 0;
while ($j <= $r) {
$r++ if defined $ret[$j];
$j++;
}
$ret[$r] = 1;
$n--;
}
print "$cnt random indexes:\n";
for (my $i = 0; $i < $n+$cnt; $i++) {
print "$i\n" if $ret[$i];
}
As you can see, the repaired algorithm does ~ $n*$cnt cycles. It is too naive. Therefore, it should be better to use some previously adviced libraries. | [reply] [d/l] |