http://www.perlmonks.org?node_id=244252

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

After doing some reading and searching on rand() I haven't quite found any documentation on something I'm trying to accomplish. I know how to randomize numbers from 0 .. 100 or from 50 .. 100 but I'm trying to randomize more than just numbers. Can someone show me the most simplistic way to randomly choose x elements from an array?

ie: @array = qw(aaron george bob tom nick heyyou john);

From this I want to randomly pick out an x number (in this case let's say we want to pull back 2) words from the list. Can someone help me figure this one out?

Thanks!

"Age is nothing more than an inaccurate number bestowed upon us at birth as just another means for others to judge and classify us"

sulfericacid

  • Comment on randomly choosing elements from an array

Replies are listed 'Best First'.
Re: randomly choosing elements from an array
by jdporter (Paladin) on Mar 19, 2003 at 06:44 UTC
    The canonical way to choose one element randomly from an array @a is
    $item = $a[ rand @a ];
    If you need to do this n times, you could therefore do this:
    my @items; for ( 1 .. $n ) { push @items, $a[ rand @a ]; }
    However, you have not said whether you can validly choose the same value twice. If not, then you'll have to remove each element from the set when you choose it. Like so:
    my @items; for ( 1 .. $n ) { push @items, splice @a, rand @a, 1; }
    Of course, if you'd prefer not to destroy @a, then work on a disposable copy of it.

    jdporter
    The 6th Rule of Perl Club is -- There is no Rule #6.

      I recently had to solve the 'randomly select n unique elements from an array' problem. The solution I hit upon was to shuffle the list and take the first n elements. The trick being, you don't have to shuffle the whole list, you just need to ensure the first n elements were shuffled:

      my @list = 'a'..'z'; # use your data in +stead of this my $n = 5; # how many do you +need? foreach my $i (0..$n-1) { my $j = rand @list; ($list[$i], $list[$j]) = ($list[$j], $list[$i]); # swap ith and jth + elements }; print join(', ', @list[0..$n-1]), "\n";

      As you say though, the original poster didn't specify whether unique selections were a requirement.

      Wow, this seems to be a lot easier than the method everyone else is aiming at. For the script I will actually require to only randomly call back unique instances of the phrase.

      Thanks so much for your help!

      "Age is nothing more than an inaccurate number bestowed upon us at birth as just another means for others to judge and classify us"

      sulfericacid

      This would seem to disallow duplicate choices:
      my @items; while (1) { my $item = $a[ rand @a ]; push @items, $item unless $item ~~ @items; last if $n <= @items; }
Re: randomly choosing elements from an array
by dws (Chancellor) on Mar 19, 2003 at 06:01 UTC
    From this I want to randomly pick out an x number (in this case let's say we want to pull back 2) words from the list.

    Introduce a level of indirection. Instead of picking from the list, pick from a set of indices into the list. Randomize the set of indices, and pick the first X.

      Sorry, I'm still what you can call perl-iliterate. I didn't understand a word you just said. Do you have any links to thinks which may help clear things up?

      Thanks.

      "Age is nothing more than an inaccurate number bestowed upon us at birth as just another means for others to judge and classify us"

      sulfericacid

        What dws mean is:
        1. create an index array containing all valid index. For example, if you have a list of 5 words, then this index array world be (0,1,2,3,4).
        2. Shuffle this inddex array randomly, so it becomes something like (3,0,4,2,1). (I guess, the difficulty to you would be how to shuffle this array ;-)
        3. Now pick the words. For example, continue from step 2, say you want pick 2 words. As the first two elements of the shuffled index array is 3 and 0, so you just pick the words at index 3 and 0 of your word list.
        Do you have any links to thinks which may help clear things up?

        pg catches my meaning. The logic you need to shuffle the indices can be found in Fisher-Yates shuffle?.

Re: randomly choosing elements from an array
by domm (Chaplain) on Mar 19, 2003 at 08:59 UTC
    What about shuffling the array using one of the shuffle functions in either List::Util or Algorithm::Numerical::Shuffle and then shifting off the desired number (or a random number) of elements?
    -- #!/usr/bin/perl for(ref bless{},just'another'perl'hacker){s-:+-$"-g&&print$_.$/}
Re: randomly choosing elements from an array
by pg (Canon) on Mar 19, 2003 at 06:12 UTC
    There are different ways to do this.

    One way is to have another array to remember which word has already been used (logically this is a bit mask), so you would not pick a used word twice (assume this is one of your requirement).

    1. For the first word, you just generate a random number within the range of array index, and pick the word at that index. Also mark it as used.
    2. Start from the second word, you generate a random number r first, if the word at index r is not used, pick it, and mark as used; if it is already used, check whether r + 1 is used, this goes on and on, until you find an unused one. Imaging the array as a ring, when you reach the end, go back to the beginning.
Re: randomly choosing elements from an array
by zby (Vicar) on Mar 19, 2003 at 09:12 UTC
Re: randomly choosing elements from an array
by Segfault (Scribe) on Mar 19, 2003 at 06:05 UTC
    Perhaps something like this?
    my @array = qw(aaron george bob tom nick heyyou john); my $numitems = int(rand($#array)); for(my $i = 0; $i < $numitems; $i++) { my $item = int(rand($#array)); print @array[$item] . "\n"; }
      The proper end index is int rand @array, not int rand $#array. For example, for an array of 7 items, rand @array would return a number that is equal to or higher than 0 or, and below 7. That means that the largest number you can ever get would be like 6.9999999999.... int truncates this to an integer towards zero (AKA "rounding down"), so the largest integer you can ever get this way, is 6.

      What's more, rand is uniformely distributed, which means that the chance of getting an outcome within any subrange is proportional to the size of that subrange, thus, the difference between upper and lower limit of the subrange — the lower edge is included in this range, the upper edge is not. Therefore, after int, the chance of getting any integer in the range is the same for all, as the size of each subrange resulting in one particular integer value is 1 — including the last one.