YAFZ has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks, I wanted to simulate drawing random numbers from a bag of numbers containing N numbers. And so did I code it as follows (N=10 for this example, I do not put the drawn number back into the bag):
#!/usr/bin/perl -w use strict; my $x; my $i; my @my_bag; @my_bag = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); $i = @my_bag + 1; while (@my_bag > 0) { $x = int(rand($i)); $x = $x > 0 ? $x - 1 : $x; print splice(@my_bag, $x, 1) . "\n"; $i--; }
I'd be glad indeed if experienced Perl monks could review my code. Is there a BETTER way to write this simulation? A faster way, a more elegant (or maybe more obfuscated ;-) one?

Maybe what I did can also be called "shuffling an array", am I wrong? Any other ways to accomplish the same effect? Broaden my vision and enlighten me :)

Replies are listed 'Best First'.
Re: Simulating Drawing From A Bag
by broquaint (Abbot) on Jun 12, 2003 at 09:08 UTC
    What you're doing there could be reduced to
    perl -MList::Util=shuffle -le 'print join $/, shuffle 1 .. 10'
    Whereas a closer simulation of drawing random numbers from a bag would be through a subroutine
    sub bag_draw { my $bag = shift; return splice(@$bag, rand(@$bag), 1); }
    This way you can draw items from a 'bag' if and when you choose. You might also be interested in the set implementation on CPAN - Set::Bag.
    HTH

    _________
    broquaint

      Glad to see the one-liner :) I'll take a look at List::Util and Set::Bag.
Re: Simulating Drawing From A Bag
by Skeeve (Parson) on Jun 12, 2003 at 09:05 UTC
    with the search "shuffling an array" you could have found this: How do I shuffle an array randomly?

    Nevertheless, regarding your code, I think there is a problem:

    $x = $x > 0 ? $x - 1 : $x;
    This will make the chance for a 0 double the chance of any other number.

    I wouldn't use splice. I'd go te way of really shuffling the array and then printing it either complete or using the elements in sequence one by one.

    For shuffling seethe link I gave above.

      Thanks for drawing my attention to the problem of probability. Since I plan to use this script for some kind of lottery it would be bad for some numbers having more probability compared to others ;-)
Re: Simulating Drawing From A Bag
by arthas (Hermit) on Jun 12, 2003 at 09:05 UTC

    This node should be useful to you to get information about shuffling your array.

    There are also may others, try Super Search!

    Michele.

Re: Simulating Drawing From A Bag
by Lachesis (Friar) on Jun 12, 2003 at 09:14 UTC
    For your code as it is.
    To save typing you can define your bag array with range; You don't need to use the $i variable at all
    Use $#bag as your argument to rand instead to generate a random number between 0 and the last index of the array
    . You also don't need to change the value of $x at all.
    This would give you
    #!/usr/bin/perl -w use strict; my @my_bag; @my_bag = (1..10); while (@my_bag > 0) { my $x = int(rand($#my_bag)); print splice(@my_bag, $x, 1) . "\n"; }
    Shuffling via rand and splice isn't very efficient though on large arrays. Have a look at perldoc -q shuffle for an example of a fisher-yates shuffle
Re: Simulating Drawing From A Bag
by bigj (Monk) on Jun 12, 2003 at 09:50 UTC
    The Tie::Pick module seems also to do what you want:
    use Tie::Pick; tie my $pick_from_bag, 'Tie::Pick', (1 .. 10); while (defined(my $element = $pick_from_bag)) { print $element, "\n"; }

    Greetings,
    Janek

Re: Simulating Drawing From A Bag
by Bilbo (Pilgrim) on Jun 12, 2003 at 09:12 UTC

    Why not just

    while (@my_bag > 0) { $x = int(rand(@my_bag)); print splice(@my_bag, $x, 1) . "\n"; }

    This avoids the problem that Skeeve has pointed out.

Re: Simulating Drawing From A Bag
by gmpassos (Priest) on Jun 13, 2003 at 06:04 UTC
    Just use sort:
    my @my_bag = qw(1 2 3 4 5 6 7 8 9 10) ; my @random = sort{ (-1,1)[rand(2)] } @my_bag ; print "@random\n" ;

    Just to play:

    my @same_order = sort{ 0 } @my_bag ; my @reverse = sort{ 1 } @my_bag ;

    Graciliano M. P.
    "The creativity is the expression of the liberty".