in reply to Picking a random item through probability

In addition to rhesa's recommendation - Your "%hash" most likely does not look the way you expect it to - There are no usable Keys, and the structure does not make logial sense for what you say you need. Here is what your structure looks like:
$VAR1 = { 'ARRAY(0x19333b0)' => [ 'baz', 5 ], 'ARRAY(0x350c8)' => [ 'bar', 7 ] };
Note that it has converted your Arrayref ["foo",1] into a KEY, which is probably not what you want. In any case, a hash-type structure does not seem to be called for , based on your description. I'd sugges an AoA.
Update: AoA Recommended:
my @Index_Is_The_Weight=( [], # Zero weight - no items ["foo"], # Only one item has weight ONE [], # No items in weight 2 ["bar"], # Three ([]) x 5, # Five empty items ["quux"] );

     "A closed mouth gathers no feet." --Unknown

Replies are listed 'Best First'.
Re^2: Picking a random item through probability
by GrandFather (Saint) on Nov 23, 2006 at 21:23 UTC

    Actully a hash does make sense if it has been used to accumulate usage statistics:

    ++$hash{$_} for @stuff;

    but, as you almost suggest, the hash should look like:

    my %hash = ( foo => 1, bar => 3, baz => 4, quux => 9, );

    DWIM is Perl's answer to Gödel

      Actually, even this is problematic. Because you need to go through the list in a defined order to ensure that each item has its respective probabilities. So you really do need the array refs ... but you need them in a list, not a hash.

      my @items = ( ["foo", 1], ["bar", 3], ["baz", 4], ["quux", 9] );
      Then, when you generate a number, you first loop through summing $_->[1]:
      use List::Util; my $total_weight = List::Util::sum(map { $_->[1] } @items);
      and then you generate a number from that, and then find the item in the total:
      my $pick = int(rand($total_weight)); my $picked_item = (List::Util::first { $pick -= $_->[1]; $pick < 0 } + @items)->[0];
      Ok, that's a bit convoluted, but I don't quite want to do someone's homework ;-) Anyway, the point is that if, when iterating over the hash, you end up with the hash being reordered from lookup to lookup, I don't think you'll get precisely the correct distribution. (Of course, I'm assuming a perfectly random rand function, but that is a separate problem.)

      Update: fix some minor formatting, and off-by-one error (was $pick <= 0)

        Woaw, impressive :) So concise, yet so clear. Not to mention that it actually works too!

        I read up about List::Util::first and I wish I knew about it earlier, that's a hell of an awesome subroutine (in fact the whole module is pretty cool, too).

        At first I was a bit puzzled about the (L::U::f)->[0] thing, but of course - all elements are in fact two elements each, and at this point of the progress we only care about the first. Nifty how you put all that in just one line without becoming too obscure.

        So much for flattering around, because there's still one part I don't quite understand. They do it in Weighted random numbers generator too. What does $pick dropping below zero have to do with anything? How does it work? I tried to figure it out by running the code mentally, but I just don't understand...

        Update: also, let it be known it's no homework :) I write Perl just as an hobby. I'm not really into computer science or whatsoever - I mean, I understand something like this can't be hard and if it would've been a homework assignment I would be ashamed I couldn't figure it out by myself :)