in reply to Select three random numbers between 1..10

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.

Replies are listed 'Best First'.
Re^2: Select three random numbers between 1..10 (nice)
by tye (Sage) on Mar 17, 2004 at 03:35 UTC

    Very nice.

    Take another look at my solution (which also doesn't deal with a list). For picking relatively few items from large ranges, my solution should be much faster than yours. But I can certainly see situations where I'd prefer yours over mine (for example, if I wanted the items returned in sorted order).

    I plan to make iterator versions of both.

    - tye