schulze-vote 2.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +50 -39
- data/lib/vote/condorcet/schulze/classifications.rb +2 -2
- data/lib/vote/condorcet/schulze/input.rb +15 -13
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3016d28cfc6bef2976750a075643923e602bd304
|
4
|
+
data.tar.gz: 5dcb2b785be020acc0a8761cda566981867e5486
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: edb55c862b09981e06dbe6e46e0b5093c622e22e3ceffa9ab2af0254081de28bc78566f91ab8756caded729ac0bc49fdf3a2aaec647a495e7ba45bb371299200
|
7
|
+
data.tar.gz: 7771b158d623a1efbc5ed29705949e7f5a297d7e29b1cbc8d6731a3986d8346e629f3bad7b7efeee54e4682b673b0c22fc56161a1671fca91adc81f27d1e79ac
|
data/LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License
|
2
2
|
|
3
|
-
Copyright (c) 2010-
|
3
|
+
Copyright (c) 2010-2016 Alessandro Rodi.
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -6,6 +6,7 @@ a type of the Condorcet voting methods.
|
|
6
6
|
## Master
|
7
7
|
|
8
8
|
[![Build Status](https://travis-ci.org/coorasse/schulze-vote.svg?branch=master)](https://travis-ci.org/coorasse/schulze-vote)
|
9
|
+
[![Gem Version](https://badge.fury.io/rb/schulze-vote.svg)](https://badge.fury.io/rb/schulze-vote)
|
9
10
|
|
10
11
|
## Develop
|
11
12
|
|
@@ -13,11 +14,6 @@ a type of the Condorcet voting methods.
|
|
13
14
|
[![Code Climate](https://codeclimate.com/github/coorasse/schulze-vote/badges/gpa.svg)](https://codeclimate.com/github/coorasse/schulze-vote)
|
14
15
|
[![Test Coverage](https://codeclimate.com/github/coorasse/schulze-vote/badges/coverage.svg)](https://codeclimate.com/github/coorasse/schulze-vote/coverage)
|
15
16
|
|
16
|
-
Wikipedia:
|
17
|
-
|
18
|
-
* [Schulze method](http://en.wikipedia.org/wiki/Schulze_method) ([deutsch](http://de.wikipedia.org/wiki/Schulze-Methode))
|
19
|
-
* [Floyd–Warshall algorithm](http://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm)
|
20
|
-
|
21
17
|
## Install
|
22
18
|
|
23
19
|
``` bash
|
@@ -34,21 +30,27 @@ gem 'schulze-vote', require: 'schulze_vote'
|
|
34
30
|
|
35
31
|
``` ruby
|
36
32
|
require 'schulze_vote'
|
37
|
-
|
38
|
-
|
33
|
+
vote_list = <<EOF
|
34
|
+
C;A;B
|
35
|
+
C;B;A
|
36
|
+
A;B;C
|
37
|
+
A;C;B
|
38
|
+
B;C;A
|
39
|
+
B;A;C
|
40
|
+
EOF
|
41
|
+
schulze_basic = SchulzeBasic.do(vote_list, candidate_count)
|
42
|
+
schulze_classifications.classification_with_ties # shows the final classification
|
39
43
|
```
|
40
44
|
|
41
|
-
`SchulzeBasic.do` - SchulzeBasic is a short term for `Vote::Condorcet::Schulze::Basic` and `.do` is a method of this class!
|
42
|
-
|
43
45
|
Input:
|
44
46
|
|
45
47
|
* `vote_list`
|
46
|
-
* Array of Arrays: votes of each voter as weights `[ [
|
47
|
-
* String: "A;B;C\
|
48
|
+
* Array of Arrays: votes of each voter as weights `[ [1,2,3],[3,2,1],[1,1,2] ]`
|
49
|
+
* String: "A;B;C\nB,A;C\n;3=C;B;A"
|
48
50
|
* File: first line **must** be a single integer, following lines like vote_list type String (see vote lists under `examples` directory)
|
49
|
-
* `candidate_count` Integer: number of
|
51
|
+
* `candidate_count` Integer: number of options
|
50
52
|
* **required** for vote_list types of Array and String
|
51
|
-
*
|
53
|
+
* leave empty if vote_list is a File
|
52
54
|
|
53
55
|
### String/File format:
|
54
56
|
|
@@ -84,14 +86,11 @@ Very easy: The reason is, that voters can leave out candidates (they give no spe
|
|
84
86
|
|
85
87
|
So, schulze-vote needs to know, how many real candidates are in the voting process.
|
86
88
|
|
87
|
-
Okay, for Array inputs it's currently a little bit overhead, because the voters array normally should have the size of the candidates count.
|
88
|
-
See it as an visual reminder while coding with this gem.
|
89
|
-
|
90
89
|
### Examples
|
91
90
|
|
92
91
|
#### Array
|
93
92
|
|
94
|
-
|
93
|
+
Only weight values, no letters here
|
95
94
|
|
96
95
|
``` ruby
|
97
96
|
require 'schulze_vote'
|
@@ -99,6 +98,20 @@ vote_list_array = [[3,2,1],[1,3,2],[3,1,2]]
|
|
99
98
|
vs = SchulzeBasic.do vote_list_array, 3
|
100
99
|
```
|
101
100
|
|
101
|
+
```
|
102
|
+
voter => A D C B
|
103
|
+
|
104
|
+
weight => 4,1,2,3
|
105
|
+
|
106
|
+
A is on first position = highest prio == 4
|
107
|
+
B is on last position == 1
|
108
|
+
C is on third position == 2
|
109
|
+
D is on second position == 3
|
110
|
+
```
|
111
|
+
|
112
|
+
Next versions will have an automatic Preference-to-Weight algorithm.
|
113
|
+
(Internally only integers are used for calculation of ranking.)
|
114
|
+
|
102
115
|
#### String
|
103
116
|
|
104
117
|
``` ruby
|
@@ -120,23 +133,8 @@ require 'schulze_vote'
|
|
120
133
|
vs = SchulzeBasic.do File.open('path/to/vote.list')
|
121
134
|
```
|
122
135
|
|
123
|
-
### _preference order to weight_ example
|
124
|
-
|
125
|
-
```
|
126
|
-
voter => A D C B
|
127
|
-
|
128
|
-
weight => 4,1,2,3
|
129
|
-
|
130
|
-
A is on first position = highest prio == 4
|
131
|
-
B is on last position == 1
|
132
|
-
C is on third position == 2
|
133
|
-
D is on second position == 3
|
134
|
-
```
|
135
|
-
|
136
|
-
Later versions will have an automatic Preference-to-Weight algorithm.
|
137
|
-
(Internally only integers are used for calculation of ranking.)
|
138
136
|
|
139
|
-
###
|
137
|
+
### SchulzeBasic
|
140
138
|
|
141
139
|
It doesn't matter if you start counting at 0 (zero) or 1 (one).
|
142
140
|
|
@@ -146,8 +144,16 @@ Internally it will only check if candidate X > candidate Y
|
|
146
144
|
|
147
145
|
Output:
|
148
146
|
|
149
|
-
* `.
|
147
|
+
* `.ranking` Array: numbers of total wins for each candidate `[candidate A, candidate B, candidate C, ...]`
|
150
148
|
* `.winners_array` Array: set 1 if the candidate is a potential winner `[candidate A, candidate B, candidate C, ...]`
|
149
|
+
* `.vote_matrix` Matrix: it contains the pairwise defeats
|
150
|
+
* `.play_matrix` Matrix: it contains the strongest paths
|
151
|
+
* `.result_matrix` Matrix: it contains the beat pairwise
|
152
|
+
* `.beat_couples` Array of Pairs: it contains the beat pairwise in array format
|
153
|
+
* `.ties` Array of Array: it contains the ties between candidates
|
154
|
+
* `.potential_winners` Array: it contains the possible winners
|
155
|
+
|
156
|
+
|
151
157
|
|
152
158
|
## Example
|
153
159
|
|
@@ -168,7 +174,7 @@ votestring = <<EOF
|
|
168
174
|
7=D;C;E;B;A
|
169
175
|
8=E;B;A;D;C
|
170
176
|
EOF
|
171
|
-
vs = SchulzeBasic.do
|
177
|
+
vs = SchulzeBasic.do(votestring, 5)
|
172
178
|
puts_m vs.vote_matrix
|
173
179
|
|
174
180
|
#=> [0, 20, 26, 30, 22]
|
@@ -204,18 +210,17 @@ end
|
|
204
210
|
#=> ["E", "A", "C", "B", "D"]
|
205
211
|
```
|
206
212
|
|
207
|
-
which is the same result of the reference above.
|
208
213
|
|
209
214
|
## Classifications
|
210
215
|
|
211
|
-
You have a `classifications(limit_results = false)` that you can call.
|
216
|
+
You have a `SchulzeClassifications.new(vs).classifications(limit_results = false)` that you can call.
|
212
217
|
If the number of results is greater then the `limit_results` parameter then a `TooManyClassificationsException`
|
213
218
|
is raised.
|
214
219
|
If you set this parameter to any value other then `false` be careful to catch and manage the exception properly.
|
215
220
|
|
216
221
|
## Classification with ties
|
217
222
|
|
218
|
-
You have a `classification_with_ties` that you can call.
|
223
|
+
You have a `SchulzeClassifications.new(vs).classification_with_ties` that you can call.
|
219
224
|
This method return a uniq classification in array of arrays format to display results on screen.
|
220
225
|
Please note that for cases like this: https://en.wikipedia.org/wiki/User:MarkusSchulze/Schulze_method_examples#Example_4
|
221
226
|
it will return the following: [[B,D], [A,C]]
|
@@ -233,12 +238,18 @@ it will return the following: [[B,D], [A,C]]
|
|
233
238
|
|
234
239
|
## Problems? Questions?
|
235
240
|
|
236
|
-
![Alessandro Rodi](http://www.gravatar.com/avatar/32d80da41830a6e6c1bb3eb977537e3e)
|
241
|
+
[![Alessandro Rodi](http://www.gravatar.com/avatar/32d80da41830a6e6c1bb3eb977537e3e)](https://github.com/coorasse)
|
237
242
|
|
238
243
|
## Thanks
|
239
244
|
|
240
245
|
Thanks to Christoph Grabo for providing the idea and base code of the gem
|
241
246
|
|
247
|
+
## Wikipedia:
|
248
|
+
|
249
|
+
* [Schulze method](http://en.wikipedia.org/wiki/Schulze_method) ([deutsch](http://de.wikipedia.org/wiki/Schulze-Methode))
|
250
|
+
* [Schulze method examples](https://en.wikipedia.org/wiki/User:MarkusSchulze/Schulze_method_examples)
|
251
|
+
* [Floyd-Warshall algorithm](http://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm)
|
252
|
+
|
242
253
|
## Copyright
|
243
254
|
|
244
255
|
See LICENSE for further details.
|
@@ -29,6 +29,8 @@ module Vote
|
|
29
29
|
add_excludeds(result)
|
30
30
|
end
|
31
31
|
|
32
|
+
private
|
33
|
+
|
32
34
|
def add_excludeds(result)
|
33
35
|
excludeds = (@schulze_basic.candidates - result.flatten) # all remaining elements (not in tie, not winners)
|
34
36
|
excludeds.each do |excluded|
|
@@ -42,8 +44,6 @@ module Vote
|
|
42
44
|
result
|
43
45
|
end
|
44
46
|
|
45
|
-
private
|
46
|
-
|
47
47
|
def better_ranking?(a, b)
|
48
48
|
@schulze_basic.ranking[a] > @schulze_basic.ranking[b]
|
49
49
|
end
|
@@ -5,10 +5,9 @@ module Vote
|
|
5
5
|
def initialize(vote_list, candidate_count = nil)
|
6
6
|
@vote_list = vote_list
|
7
7
|
@candidate_count = candidate_count
|
8
|
-
|
8
|
+
@candidates_abc = []
|
9
9
|
if @candidate_count.nil?
|
10
10
|
insert_vote_file(@vote_list) if vote_list.is_a?(File)
|
11
|
-
|
12
11
|
else
|
13
12
|
@vote_matrix = ::Matrix.scalar(@candidate_count, 0).extend(Vote::Matrix)
|
14
13
|
insert_vote_array(@vote_list) if vote_list.is_a?(Array)
|
@@ -32,27 +31,30 @@ module Vote
|
|
32
31
|
vs.split(/\n|\n\r|\r/).each do |voter|
|
33
32
|
voter = voter.split(/=/)
|
34
33
|
vcount = (voter.size == 1) ? 1 : voter[0].to_i
|
35
|
-
|
36
34
|
vcount.times do
|
37
35
|
tmp = voter.last.split(/;/)
|
38
36
|
vote_array << extract_vote_string(tmp)
|
39
37
|
end
|
40
38
|
end
|
41
|
-
|
42
39
|
insert_vote_array vote_array
|
43
40
|
end
|
44
41
|
|
45
|
-
def extract_vote_string(tmp)
|
42
|
+
def extract_vote_string(tmp) # array of preferences [['1, 2'], ['3']. ['4, 5']]
|
43
|
+
tmp2 = flatten_votes(order_and_remap(tmp))
|
44
|
+
tmp2.map! { |e| [e[0].to_i, e[1]] } if all_numbers?(tmp2)
|
45
|
+
tmp2.sort.map { |e| e[1] } # order, strip & add
|
46
|
+
end
|
47
|
+
|
48
|
+
def flatten_votes(votes)
|
46
49
|
tmp2 = []
|
47
|
-
|
48
|
-
|
49
|
-
if e[0].size > 1
|
50
|
-
e[0].split(/,/).each { |f| tmp2 << [f, e[1]] }
|
51
|
-
else
|
52
|
-
tmp2 << e
|
53
|
-
end
|
50
|
+
votes.map do |e| # find equal-weighted candidates
|
51
|
+
(e[0].size > 1) ? e[0].split(/,/).each { |f| tmp2 << [f, e[1]] } : tmp2 << e
|
54
52
|
end
|
55
|
-
tmp2
|
53
|
+
tmp2
|
54
|
+
end
|
55
|
+
|
56
|
+
def all_numbers?(array)
|
57
|
+
array.map { |e| e[0] }.all? { |el| /\A\d+\z/.match(el) }
|
56
58
|
end
|
57
59
|
|
58
60
|
def order_and_remap(tmp)
|