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

i need to create a few lists of unique random numbers

## set %availableNumbers %availableNumbers =(); for ($i)5{) { $availableNumbers{$i} = 1; } ## create lists in a foreach loop %lists = (); @listIDs = (1,2,3,4,5,6,7,8,9,10); $itemsPerList = 10; foreach $listID (@listIDs) { my %myAvailableNumbers = %availableNumbers; for ($i=1;$i<=$itemsPerList;$i++){ @myAvailableNumbers = keys %myAvailableNumbers; my $number = $myAvailableNumbers[rand($#myAvailableNumbers)]; push @{$lists{$listID}}, $number; delete $myAvailableNumbers{$number}; } }
so that works

i just intensely dislike the:
@myAvailableNumbers = keys %myAvailableNumbers; line anyone know how i can rewrite that section to get rid of it?

Replies are listed 'Best First'.
Re: unique random numbers
by Zaxo (Archbishop) on Sep 17, 2002 at 02:43 UTC

    Deleting a hash element returns its value, so if you define the %availableNumbers hash like this:

    my ($lidno, $perbin, %lists) = (10, 10); my %availableNumbers = map { $_ => $_ } 1..$lidno*$perbin;
    you get to choose and delete at the same time.

    for my $lid (1..$lidno) { my %deck = %availableNumbers; $lists{$lidno} = [ map { delete %deck{ (keys %deck)[rand( keys %deck)]} } 1..$perbin ]; }

    That could have been folded into another map, but enough is enough ;-)

    After Compline,
    Zaxo

Re: unique random numbers
by BrowserUk (Patriarch) on Sep 17, 2002 at 07:10 UTC

    There is no need to use hashes at all. Just shuffle your list of available numbers then move them in chunks of 10 (or whatever) to a list of lists using splice. Then just pop one off when you need it.

    #! perl -sw use strict; # get available numbers from somewhere. my @available = (100..999); # shuffle them $a = $_ + rand @available - $_ and @available[$_, $a] = @available[$a, + $_] for (0..$#available); my @lists; # move the randomly distributed numbers into 10 lists 10 at a time. $lists[$_] = [ splice( @available, 0, 10) ] for 1..10; # to use and discard the next number from the 5th list my $next = pop @{$lists[5]}; print "list[$_]=( @{$lists[$_]} )\n" for 1..10; __END__ # Output Note: The number used and discarded from the 5th list. C:\test>198405.pl C:\test>198405.pl list[1]=( 473 849 328 853 535 166 196 138 466 553 ) list[2]=( 998 533 640 693 564 555 498 766 614 294 ) list[3]=( 527 195 722 886 751 458 134 186 246 556 ) list[4]=( 461 120 694 440 387 221 938 862 971 439 ) list[5]=( 457 957 446 976 397 403 425 748 966 ) list[6]=( 870 877 139 776 496 271 378 811 565 472 ) list[7]=( 942 414 463 358 146 958 168 728 158 338 ) list[8]=( 162 169 509 268 815 716 410 629 965 705 ) list[9]=( 842 468 203 412 651 130 343 539 219 177 ) list[10]=( 734 224 275 658 127 810 874 649 844 633 ) C:\test>

    Well It's better than the Abottoire, but Yorkshire!
Re: unique random numbers
by dug (Chaplain) on Sep 17, 2002 at 03:05 UTC
    i just intensely dislike the: @myAvailableNumbers = keys %myAvailableNumbers; line anyone know how i can rewrite that section to get rid of it?

    If you are using @array = keys %hash; only so you can use $#array later, you can use either of the methods in the script below. The fist one uses the interpolation trick from Recipe 1.10 in the Cookbook, which isn't very readable. The second test is pretty straight forward.
    #!/usr/bin/perl -w use strict; $|++; use Test::Simple tests => 2; my $size = int( rand(500) ); my %hash = map { $_ => 1 } my @array = ( 0 .. $size ); ok( $#array == (@{[ %hash ]})/2 - 1, 'Recipe 1.10 from the Cookbook' ) +; ok( $#array == keys(%hash)-1, 'keys minus 1' );
    Zaxo++ for his elegan solution to the problem at hand.

      dug