Instant runoff voting...

dpuu on 2008-12-24T22:16:35

Advanced Challenge 3...

A couple of different approaches here: one is to maintain a list of excluded candidates, and then ignore them during recounts. The other is to actually delete them from the ballot and then recount. Both solutions work: the first uses junctions; the other uses the "is rw" trait on a for loop. So both are of some interest.

First: the junctional approach...

my @ballots = ( [< A B C D >], [< B D C A >], [< C D B A >], [< B A C D >], [< A D C B >], );

my $winner; my @excluded;

while ! $winner {

my %tallies; for @ballots -> @ballot { if @ballot.first: { $_ ne all(@excluded) } -> $chosen { %tallies{ $chosen } ++; } }

my $total = [+] %tallies.values;

if %tallies.pairs.first: { .value > $total/2 } -> $kv { $winner = $kv.key; } else { my $losing_tally = min %tallies.values; for %tallies.pairs.grep: { .value == $losing_tally } -> $kv { my $loser = $kv.key; say "exclude $loser"; @excluded.push: $loser; } } }

say "WINNER IS: $winner";


That wasn't too hard. Note the "pointy-block" used with the "if" statement -- this syntax isn't limited to "for" loops!

The other way is almost identical:



my $winner;

while ! $winner {

my %tallies; for @ballots.grep: {.elems} -> @ballot { %tallies{ @ballot[0] } ++; }

my $total = [+] %tallies.values;

if %tallies.pairs.first: { .value > $total/2 } -> $kv { $winner = $kv.key; } else { my $losing_tally = min %tallies.values; for %tallies.pairs.grep: { .value == $losing_tally } -> $kv { my $loser = $kv.key; say "exclude $loser"; for @ballots -> @ballot is rw { @ballot = @ballot.grep: { $_ ne $loser } } } } }

say "WINNER IS: $winner";


Again, no problems.

Two things were "slightly harder than they should be".

First, the "inverse lookup" of a hash key from its value seems a little cumbersome. Sure, there's probably no getting round the need to iterate over all the values: but wouldn't it be nice the perl took care of that for me. In the first solution I did this iteration as a "for" loop; in the latter I used ".first" and ".grep" methods

The other niggle is that the "dot-assign" concept doesn't seem to exist yet. Theoretically I should have been able to write:

@ballot.=grep: { $_ ne $loser }

to do the ballot modification for exclusion. Hopefully that'll be implemented in the fullness of time.