This gem is a Ruby implementation of the Schulze voting method (with help of the Floyd–Warshall algorithm), a type of the Condorcet voting methods.
It's usable, but not production grade.
Wikipedia:
Install
gem install vote-schulze
Usage
require 'vote-schulze'
vs = Vote::Schulze::Basic.call(vote_list, candidate_count)
vs.ranks
vs.ranking_abc
Input:
-
vote_list
- Array of Arrays: votes of each voter as weights
[ [A,B,C,...],[A,B,C,...],[A,B,C,...] ]
- String: "A;B;C\nA;B;C\n;3=A;B;C..."
- File: first line must be a single integer, following lines like vote_list type String (see vote lists under
examples
directory)
- Array of Arrays: votes of each voter as weights
-
candidate_count
Integer: number of candidates- required for vote_list types of Array and String
- leave empty if vote_list is a File handle!
String/File format:
A typical voters line looks like this:
A;B;C;D;E;F
You also can say that n voters have the same preferences:
n=F;E;D;C;B;A
where n is an integer value for the count.
Also it's possible to say that a voter has candidates equally weighted:
A,B;C,D;E,F
which means, that A + B, C + D and E + F are on the same weight level.
Here only 3 weight levels are used: (A,B) = 3, (C,D) = 2, (E,F) = 1
Why I must write the candidate count in the first line of the vote file?
or: Why I must give a candidate count value for Array/String inputs?
Very easy: The reason is, that voters can leave out candidates (they give no special preferences).
So, vote-schulze needs to know, how many real candidates are in the voting process.
Okay, for Array inputs it's currently a little bit overhead, because the voters array normally should have the size of the candidates count. See it as an visual reminder while coding with this gem.
Examples
Array
(Only weight values, no letters here! See section "preference order to weight example")
require 'vote-schulze'
vote_list_array = [[3,2,1],[1,3,2],[3,1,2]]
vs = Vote::Schulze::Basic.call(vote_list_array, 3)
vs.ranking_abc #=> result
String
require 'vote-schulze'
vote_list_string = <<EOF
A;B;C
B;C;A
A;C;B
A,C,B
4=C;A;B
EOF
vs = Vote::Schulze::Basic.call(vote_list_string, 3)
vs.ranking_abc #=> result
File
require 'vote-schulze'
voting_list = File.open('path/to/vote.list')
vs = Vote::Schulze::Basic.call(voting_list)
vs.ranking_abc #=> result
preference order to weight example
voter => A D C B
weight => 4,1,2,3
A is on first position = highest prio == 4
B is on last position == 1
C is on third position == 2
D is on second position == 3
Later versions will have an automatic Preference-to-Weight algorithm. (Internally only integers are used for calculation of ranking.)
It doesn't matter if you start counting at 0 (zero) or 1 (one).
Also it's not important, if you use jumps (like 1 3 5 9
).
Internally it will only check if candidate X > candidate Y
Example
Reference calculation: Schulze Methode | blog.cgiesel.de (german)
Example file under examples/vote4.list
Result should be:
voting_list = File.open('examples/vote4.list')
voting = Vote::Schulze::Basic.call(voting_list)
voting.ranking_abc
#=> ["C:1", "D:2", "B:3", "A:4"]
which is the same result of the reference above.
The result strings are always in format Candidate:Position
, because it's possible that multiple candidates can be on the same rank.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/asaaki/vote-schulze. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
Code of Conduct
Everyone interacting in the vote-schulze project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.
License
The gem is available as open source under the terms of the MIT License.