Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

getting random number 8 times never the same

by Anonymous Monk
on Oct 31, 2010 at 16:23 UTC ( [id://868601]=perlquestion: print w/replies, xml ) Need Help??

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

Replies are listed 'Best First'.
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

Re: getting random number 8 times never the same
by ikegami (Patriarch) on Oct 31, 2010 at 17:04 UTC

    The following would do the trick:

    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];
      Perfect, both worked, but yours used less code, very nice. thanks!
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.
      For long lists, that isn't more efficient than shuffling. Shuffling means going through the list once, swapping each element. Splicing means going through (on average) half the list in each iteration moving the element one to the left.

      Splicing a random element out of an array takes on average linear time.

        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.
Re: getting random number 8 times never the same
by davies (Prior) on Oct 31, 2010 at 18:12 UTC
    This is the standard "pack of cards" algorithm that I mentioned in Re: Random data generation., misunderstanding BrowserUk's question. Changing the code for your case would give:
    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
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
      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
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); }
Re: getting random number 8 times never the same
by bduggan (Pilgrim) on Nov 01, 2010 at 17:27 UTC
    You could use a reservoir sampling algorithm (see also perldoc -q random) :
    #!/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
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:

    1. “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.)
    2. “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.
    3. 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.)

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; } }

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://868601]
Approved by McDarren
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (4)
As of 2024-03-29 04:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found