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
-
Are you posting in the right place? Check out Where do I post X? to know for sure.
-
Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
<code> <a> <b> <big>
<blockquote> <br /> <dd>
<dl> <dt> <em> <font>
<h1> <h2> <h3> <h4>
<h5> <h6> <hr /> <i>
<li> <nbsp> <ol> <p>
<small> <strike> <strong>
<sub> <sup> <table>
<td> <th> <tr> <tt>
<u> <ul>
-
Snippets of code should be wrapped in
<code> tags not
<pre> tags. In fact, <pre>
tags should generally be avoided. If they must
be used, extreme care should be
taken to ensure that their contents do not
have long lines (<70 chars), in order to prevent
horizontal scrolling (and possible janitor
intervention).
-
Want more info? How to link
or How to display code and escape characters
are good places to start.