I was just looking at the code here and it struck me that there must be a simpler way than having to mess with lists.

Suppose I have to pick just one random number from the range 1..10. I can do it by using the following code:

sub pick_one { my($from,$to) = @_; for(my $i=$from;$i<=$to;$i++) { return $i if(rand(1) <= 1/($to-$i+1)); } }

(Notice that when $i==$to the test must always succeed so we don't have to worry about a final return)

If I have to pick $n more numbers and I have ($to-$from+1) numbers left then the chance that the next number will be one of those picked is ($n/($to-$from+1)), so the final form of the subroutine is:

sub pick_n { my($n,$from,$to) = @_; my(@picked); for(my $i=$from;$i<=$to;$i++) { if(rand(1) <= ($n/($to-$i+1))) { push(@picked,$i); $n--; } } return @picked; }

Without having to shuffle lists or even use them. Notice the time is always the proportional to the range and the results are always unique. Just to prove that the results are fair:

my(@counts); for(my $i=0;$i<100000;$i++) { foreach my $c (pick_n(3,1,10)) { $counts[$c]++; } } for(my $c=0;$c<=$#counts;$c++) { print "Count[$c] = $counts[$c]\n"; }

=>

Count[0] = Count[1] = 29974 Count[2] = 30044 Count[3] = 29860 Count[4] = 29949 Count[5] = 30167 Count[6] = 30253 Count[7] = 29808 Count[8] = 30092 Count[9] = 29830 Count[10] = 30028

Update: This algorithm will work well if there are lots of numbers to be selected from a shorter list. If selecting fewer numbers from longer lists then the simple hash approach that most other suggestions are based on will be more efficient.


In reply to Re: Select three random numbers between 1..10 by hawtin
in thread Select three random numbers between 1..10 by Perl_User

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.