#! perl -slw use strict; use Data::Dump qw[ pp ]; our $N ||= 1e4; sub pickGen { my $scale = shift; my $ref = @_ > 1 ? \@_ : $_[ 0 ]; my $total = 0; $total += $_ for @$ref; my $pickStick = pack 'C*', map{ ($_) x ( $scale * $ref->[ $_ ] * 10 / $total ) } 0 .. $#$ref; return sub { return ord substr $pickStick, rand length $pickStick, 1; }; } my @items = 'A' ..'C'; ## Your "difficult" weights: my @weights = map sqrt $_, 2, 5, 11; ## Only required for testing my $total = 0; $total += $_ for @weights; print "Required weights(%):\n\t", join"\t", map $_ *100 / $total, @weights; for my $scale ( 1, 10, 100 ) { ## Generate a picker (cost amortized over $N uses) my $picker = pickGen $scale, @weights; ## use it $N times my %stats; $stats{ $items[ $picker->() ] }++ for 1 .. $N; ## Check the results print "\nResults for $N picks (scale:$scale)\n\t", join "\t\t\t", map $stats{ $_ } *100 / $N, @items; } #### c:\test>weightedPick.pl -N=1e3 Required weights(%): 20.2990178902944 32.0955653989182 47.6054167107874 Results for 1e3 picks (scale:1) 21.5 34 44.5 Results for 1e3 picks (scale:10) 19.4 32.5 48.1 Results for 1e3 picks (scale:100) 19.5 29.5 51 c:\test>weightedPick.pl -N=1e6 Required weights(%): 20.2990178902944 32.0955653989182 47.6054167107874 Results for 1e6 picks (scale:1) 22.2248 33.3686 44.4066 Results for 1e6 picks (scale:10) 20.2238 32.3209 47.4553 Results for 1e6 picks (scale:100) 20.1913 32.0977 47.711