schulze-vote 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
[](https://travis-ci.org/coorasse/schulze-vote)
|
9
|
+
[](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
|
[](https://codeclimate.com/github/coorasse/schulze-vote)
|
14
15
|
[](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
|
-

|
241
|
+
[](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)
|