http://qs321.pair.com?node_id=654527


in reply to Select value from array w/o replacement (or destroying the array)

I think tye's solution is the best. If for some reason you would prefer one of the others, the answer would depend on how big n is compared to the size of the array. If it is small, say, smaller than 10%, then just pick a random number in the range 0..$#a and pick another one if that had been picked before. Such collisions should be rare for small n / @a; If it is larger, then shuffle first, but do not shuffle the array itself, especially if the array elements themselves tend to be big (which doesn't seem to be the case here). Instead, create a separate index array and shuffle that -- something like @indices = shuffle 0..$#a. Below, I tried to add comments to tye's code. I may have messed up, so wait until wiser monks have had a chance to peruse it.

use strict; use warnings; ## How many elements to pick use constant N => 10; ## The original array that must not be altered my @letters = ('a'..'z'); ##-----------------------------------------------------+ ## Generate code that will pick an element from ## @letters at each iteration without repeating ##-----------------------------------------------------+ my $picker_of_next_rand = gen_picker( \@letters ); ## Now execute (iterate) that code N times for (1..N) { print $picker_of_next_rand->(); } print "\n"; ## look nice print @letters, "\n"; ## original untouched exit( 0 ); ##-----------------------------------------------------+ ## This sub generates and returns code that will pick ## a random element from @$aref at each iteration ## without repeating ##-----------------------------------------------------+ sub gen_picker { my ($aref) = @_; my @indices = 0 .. $#$aref; ## closes on @indices return sub { if (! @indices) { die "No more items left to pick from"; } ##---------------------------------------------+ ## pick an int in the range (0 .. $#$aref), ## the range of the array's indices ##---------------------------------------------+ my $random_index = int rand @indices; ## $pick is the element at the random_index my $pick = $aref->[ $indices[ $random_index ] ]; ##---------------------------------------------+ ## We are about to pop the index array. Save ## the element at the pop-end of the index array ## by copying it into the position of the $pick ## (clobber index of $pick, won't be used again) ##---------------------------------------------+ $indices[ $random_index ] = $indices[ -1 ]; ## Now it is safe to pop the index array pop @indices; return $pick; }; }

which prints something like the following:

vkdbfymxie abcdefghijklmnopqrstuvwxyz
Server Error (Error ID 3559152a2051870)

An error has occurred. The site administrators have been notified of the problem and will likely soon fix it. We thank you, for you're patients.