where $weight is the weight factor for the current item. You can find the rationale behind the formula in that post.-log(1 - rand)/$weight
For one item, you can reduce the code to picking the item where this expression has the lowest value. There's no need to actually sort them all.
Here is code for the reduced problem:
For better randomness of a huge number of draws, it's better to pick a random number generator that has a higher number of bits (and a larger cycle) than the default one (only 15 bits on ActivePerl/Windows!), for example Math::Random::MT which can be used as a plug in replacement if you import (and override) rand.my @matrix = ( [a => 0.1], [b => 0.5], [c => 0.4] ); my $chosen = pick(@matrix); sub pick { my($choice, $threshold); foreach(@_) { my $rand = -log(1 - rand)/$_->[1]; if(not defined $threshold or $rand < $threshold) { $choice = $_->[0]; $threshold = $rand; } } return $choice; }
use Math::Random::MT qw(rand);
To demonstrate that it works as advertised, here are the results of a test run of 100_000 draws:
Output:my %picked; for(1 .. 1E5) { $picked{pick(@matrix)}++; } use Data::Dumper; print Dumper \%picked;
which looks very close to the desired distribution, to me.$VAR1 = { 'c' => 40200, 'a' => 10042, 'b' => 49758 };
p.s. If there can be any items with weight 0, thus, that should never be picked, you will have to skip them at the top of the loop, because otherwise, the expression will barf (division by zero).
In reply to Re: How do I get a random index with weighting?
by bart
in thread How do I get a random index with weighting?
by m_al
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |