Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:
I want to get 8 random numbers from a list, from these:
4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
so I guess I would put those into an array:
@nums = (4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20);
Now, how do I get a random one that is unique from each other in 8 different variables? for example:
$_num1 = join("", @nums[ map { rand @nums } ( 1 .. 1 ) ]);
Now how would I remove that number from the array? Is that the best way to get a random number from the list?
thanks,
Rich
Re: getting random number 8 times never the same
by McDarren (Abbot) on Oct 31, 2010 at 16:37 UTC
|
You could try something like this:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper::Simple;
use List::Util qw/shuffle/;
my @nums = (4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20);
my @shuffled_nums = shuffle(@nums);
my %extracted_nums;
for (0 .. 7) {
$extracted_nums{$_} = shift @shuffled_nums;
}
print Dumper(%extracted_nums);
print Dumper(@shuffled_nums);
%extracted_nums will contain the numbers removed from the original list, and @shuffled_nums will contain those left over.
Is that what you wanted?
Cheers,
Darren | [reply] [Watch: Dir/Any] [d/l] [select] |
Re: getting random number 8 times never the same
by ikegami (Patriarch) on Oct 31, 2010 at 17:04 UTC
|
my @selection = @nums;
for (0..7) {
my $i = $_ + rand(@selection - $_);
($selection[$_], $selection[$i]) = ($selection[$i], $selection[$_])
}
splice(@selection, 8);
But it would be better (simpler, cleaner, faster) to just use List::Util's shuffle.
use List::Util qw( shuffle );
my @selection = (shuffle(@nums))[0..7];
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
Perfect, both worked, but yours used less code, very nice. thanks!
| [reply] [Watch: Dir/Any] |
Re: getting random number 8 times never the same
by BrowserUk (Patriarch) on Oct 31, 2010 at 17:16 UTC
|
Depending on how big the list is, this might be more efficient than shuffling the whole list for a small number of picks:
my @a = 4..20;;
my @s = map splice( @a, rand( @a ), 1 ), 1 .. 8;;
print "@a\n@s";;
4 5 7 8 9 12 13 14 18
11 10 6 20 15 17 19 16
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [Watch: Dir/Any] [d/l] |
|
| [reply] [Watch: Dir/Any] |
|
For long lists, that isn't more efficient than shuffling.
Regardless of the length of the list, which is quicker depends upon the ratio of picks.
...for a small number of picks, Ie. If the ratio of picks to list size is less than ~15%, spliceing is quicker than shuffling the whole list:
#! perl -slw
use strict;
use List::Util qw[ shuffle ];
use Benchmark qw[ cmpthese ];
our $N //= 20;
our $S //= 8;
our @nums = 0 .. $N;
cmpthese -1, {
shuffle => q[ my @s = ( shuffle @nums )[ 0 .. $S-1 ]; ],
splice => q[ my @s = map splice( @nums, rand( @nums ), 1 ), 1 .. $
+S; ],
};
__END__
c:\test>868601 -N=10 -S=2
Rate shuffle splice
shuffle 894654/s -- -10%
splice 993686/s 11% --
c:\test>868601 -N=10 -S=3
Rate splice shuffle
splice 769417/s -- -9%
shuffle 844675/s 10% --
c:\test>868601 -N=100 -S=15
Rate shuffle splice
shuffle 196571/s -- -5%
splice 207873/s 6% --
c:\test>868601 -N=100 -S=17
Rate splice shuffle
splice 186995/s -- -3%
shuffle 192359/s 3% --
c:\test>868601 -N=1000 -S=169
Rate shuffle splice
shuffle 19552/s -- -2%
splice 19968/s 2% --
c:\test>868601 -N=1000 -S=170
Rate splice shuffle
splice 20274/s -- -1%
shuffle 20569/s 1% --
c:\test>868601 -N=10000 -S=1998
Rate shuffle splice
shuffle 1578/s -- -6%
splice 1674/s 6% --
c:\test>868601 -N=10000 -S=1999
Rate splice shuffle
splice 1601/s -- -1%
shuffle 1625/s 2% --
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [Watch: Dir/Any] [d/l] |
Re: getting random number 8 times never the same
by davies (Prior) on Oct 31, 2010 at 18:12 UTC
|
use strict;
use warnings;
my $nRepeats = 1;
my @sSet = (4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20);
my $nLength = 8;
my @sAll;
for (1..$nRepeats) {
push (@sAll, @sSet);
}
if ($nLength - 1 > $#sAll) {die "Don't be silly"}
my $sString;
for (1..$nLength) {
my $i = int(rand(@sAll));
$sString .= $sAll[$i];
$sAll[$i] = $sAll[-1];
pop(@sAll);
}
print "$sString \n";
I haven't tested this variation, but I tested the original.
Regards,
John Davies | [reply] [Watch: Dir/Any] [d/l] |
Re: getting random number 8 times never the same
by aquarium (Curate) on Oct 31, 2010 at 22:25 UTC
|
if your list is always a continuous range, as per the example, then a non-list solution would suffice.
for example the famous one liner
perl -le '$n=10; $min=5; $max=15; $, = " "; print map { int(rand($max-
+$min))+$min } 1..$n'
comes to mind
the hardest line to type correctly is: stty erase ^H
| [reply] [Watch: Dir/Any] [d/l] |
|
These random numbers would not necessarily be unique
> perl -le "$n=10;$min=5;$max=15;$,=qq( );print map {int(rand($max-$mi
+n)+$min)} 1..$n"
9 9 12 10 11 12 9 5 10 9
| [reply] [Watch: Dir/Any] [d/l] |
Re: getting random number 8 times never the same
by Khen1950fx (Canon) on Nov 01, 2010 at 06:12 UTC
|
To get 8 random numbers from the list, I used random.
It requires 5.10 though...
#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
use random qw(integer);
use Data::Dumper::Concise;
my @array = (4 .. 20);
for (0 .. 7) {
my $array = 4 + rand 20;
print Dumper($array);
}
| [reply] [Watch: Dir/Any] [d/l] |
Re: getting random number 8 times never the same
by bduggan (Pilgrim) on Nov 01, 2010 at 17:27 UTC
|
#!/usr/bin/env perl
my $k = shift @ARGV;
srand;
while (<>) {
$. <= $k and do { push @lines, $_; next; };
($a=rand($.)) < $k and ($lines[$a] = $_);
}
print @lines;
For instance,
$ seq 4 20 | ./rand.pl 8
19
5
6
14
16
9
15
11
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: getting random number 8 times never the same
by sundialsvc4 (Abbot) on Nov 02, 2010 at 00:14 UTC
|
Over the years, I have found that there are three ways of looking at this problem, all three of which are appropriate in different situations:
-
“Fuhgeddaboudit!”
Don’t borrow trouble. If the domain of random numbers is big enough (such as, “a 32-bit integer”), and the pseudo-random number generator (PRNG) is good enough (it is...), then the possibility of getting the same number twice in a reasonable length of time is near-zero. (If it were not so, then neither Monaco nor Las Vegas would still be full of casinos.)
-
“Don’t worry about checking.” If the domain is small, but the number-count that you actually need is also small, then the practical price to be paid by brute-force checking for dupes is also acceptably small.
-
If the problem that you are dealing with, is approximately equal to “dealing from a deck of cards,” then ... do that. Initialize an array of “all possible values,” then shuffle it and “deal cards” from it. (As noted, you don’t have to invent your own card-shuffling algorithm.)
| [reply] [Watch: Dir/Any] |
Re: getting random number 8 times never the same
by Khen1950fx (Canon) on Nov 02, 2010 at 12:48 UTC
|
I've been working on this all day. I based it on
this. It works:).
#!/usr/bin/perl
use strict;
use warnings;
my $length = 1;
my $max = 1;
my @strings;
for ( 1 .. $max ) {
push @strings, rand_nums($length);
}
print "@strings", "\n";
{
my %cache;
sub rand_nums {
my %local_cache;
my ($length) = 8;
my $lower = 4;
my $upper = 21;
my $serial = int( rand($upper - $lower) ) + $lower;
$local_cache{$serial} = 1;
for ( 2 .. $length ) {
my $num = int( rand($upper - $lower) ) + $lower;
redo if exists $local_cache{$num};
$local_cache{$num} = 1;
$serial .= "-$num";
}
rand_nums($length) if exists $cache{$serial};
$cache{$serial} = 1;
return $serial;
}
}
| [reply] [Watch: Dir/Any] [d/l] |
|
|