schulze-vote 2.0.6 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 54a125030f5f284e82f05d0a65c077e463a37ad0
4
- data.tar.gz: cfa32855bdd40cd8ec4c92b1d96d17f81b635d68
3
+ metadata.gz: 53d7c71ae5e43fcd9a28bf01eaf4e7f16819f5b0
4
+ data.tar.gz: 989df507edb37c37e043891c28224b643572b0ea
5
5
  SHA512:
6
- metadata.gz: 84fb23bc8171ba68ea054785d85ac7215a0372327f55b26df9b305957b0172970faa97cfdfe9f24a63f52cf572d733294bb044628f63aec8322c1d2f27f09461
7
- data.tar.gz: ed14fffb19e893095dbbd02e337dd4fabe8eb021ce0e7d5d0efa340278bf756afb993f79f4a831878be06cba74b16f4780445b98f4ff449bbfc934aa177f5a57
6
+ metadata.gz: 1082070bf51ba9aa979670a21240bc0a42efad17ee2eb385284ed2e5386f850dd537d4f837a04cdf766238cd70e691665615f478366d4735e357fa389da89c30
7
+ data.tar.gz: c2b409116e6066aebc43250aae94081681d70d700e65738dc246beeacca6c313628d2c19570ed040276a7802ec5a9a50c46ce7df0604cda8bf4adae49a26924a
data/README.md CHANGED
@@ -27,7 +27,6 @@ gem 'schulze-vote', require: 'schulze_vote'
27
27
  require 'schulze_vote'
28
28
  vs = SchulzeBasic.do vote_list, candidate_count
29
29
  vs.ranks
30
- vs.ranks_abc
31
30
  ```
32
31
 
33
32
  `SchulzeBasic.do` - SchulzeBasic is a short term for `Vote::Condorcet::Schulze::Basic` and `.do` is a method of this class!
@@ -205,6 +204,13 @@ If the number of results is greater then the `limit_results` parameter then a `T
205
204
  is raised.
206
205
  If you set this parameter to any value other then `false` be careful to catch and manage the exception properly.
207
206
 
207
+ ## Classification with ties
208
+
209
+ You have a `classification_with_ties` that you can call.
210
+ This method return a uniq classification in array of arrays format to display results on screen.
211
+ Please note that for cases like this: https://en.wikipedia.org/wiki/User:MarkusSchulze/Schulze_method_examples#Example_4
212
+ it will return the following: [[B,D], [A,C]]
213
+
208
214
  ## Contributing to schulze-vote
209
215
 
210
216
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
@@ -21,6 +21,7 @@ module Vote
21
21
  end
22
22
  @vote_matrix = input.matrix
23
23
  @candidate_count = input.candidates
24
+ @candidates = (0..@candidate_count - 1).to_a
24
25
  @vote_count = input.voters
25
26
  self
26
27
  end
@@ -30,6 +31,7 @@ module Vote
30
31
  result
31
32
  calculate_winners
32
33
  rank
34
+ calculate_beat_couples
33
35
  end
34
36
 
35
37
  attr_reader :vote_matrix
@@ -50,7 +52,7 @@ module Vote
50
52
  attr_reader :winners_array
51
53
 
52
54
  # compute all possible solutions
53
- # since this can take days, there is an option to limit the numeber of calculated classifications
55
+ # since this can take days, there is an option to limit the number of calculated classifications
54
56
  # the default is 10. if the system is calculating more then 10 possible classifications it will stop
55
57
  # raising a TooManyClassifications exception
56
58
  # you can set it to false to disable the limit
@@ -58,8 +60,29 @@ module Vote
58
60
  @classifications ||= calculate_classifications(limit_results)
59
61
  end
60
62
 
61
- def beat_couples
62
- @beat_couples ||= calculate_beat_couples
63
+ attr_reader :beat_couples
64
+
65
+ attr_reader :ties
66
+
67
+ # compute the final classification with ties included
68
+ # the result is an array of arrays. each position can contain one or more elements in tie
69
+ # e.g. [[0,1], [2,3], [4], [5]]
70
+ def classification_with_ties
71
+ calculate_potential_winners
72
+ result = []
73
+ result << @potential_winners # add potential winners on first place
74
+ result += @ties.clone.sort_by { |tie| -@ranking[tie[0]] } # add all the ties ordered by ranking
75
+ result.uniq! # remove duplicates (potential winners are also ties)
76
+ excludeds = (@candidates - result.flatten) # all remaining elements (not in a tie and not winners)
77
+ excludeds.each do |excluded|
78
+ result.each_with_index do |position, index|
79
+ # insert before another element if they have a better ranking
80
+ break result.insert(index, [excluded]) if has_better_ranking?(excluded, position[0])
81
+ # insert at the end if it's the last possible position
82
+ break result.insert(-1, [excluded]) if index == result.size - 1
83
+ end
84
+ end
85
+ result
63
86
  end
64
87
 
65
88
  private
@@ -126,17 +149,35 @@ module Vote
126
149
  @potential_winners
127
150
  end
128
151
 
152
+ # calculates @beat_couples and @ties in roder to display results afterward
129
153
  def calculate_beat_couples
130
- beat_couples = []
154
+ return if @calculated_beat_couples
155
+ @beat_couples = []
156
+ @ties = []
131
157
  ranks.each_with_index do |_val, idx|
132
158
  ranks.each_with_index do |_val2, idx2|
133
159
  next if idx == idx2
134
- if play_matrix[idx, idx2] > play_matrix[idx2, idx]
135
- beat_couples << [idx, idx2]
136
- end
160
+ next @beat_couples << [idx, idx2] if play_matrix[idx, idx2] > play_matrix[idx2, idx]
161
+ next unless in_tie?(idx, idx2)
162
+ next if @ties.any? { |tie| ([idx, idx2] - tie).empty? }
163
+ tie = @ties.find { |tie| tie.any? { |el| el == idx } }
164
+ next tie << idx2 if tie
165
+ tie = @ties.find { |tie| tie.any? { |el| el == idx2 } }
166
+ next tie << idx if tie
167
+ @ties << [idx, idx2]
137
168
  end
138
169
  end
139
- beat_couples
170
+ @calculated_beat_couples = true
171
+ end
172
+
173
+ def in_tie?(idx, idx2)
174
+ play_matrix[idx, idx2] == play_matrix[idx2, idx] &&
175
+ @ranking[idx] == @ranking[idx2] &&
176
+ @winners_array[idx] == @winners_array[idx2]
177
+ end
178
+
179
+ def has_better_ranking?(a, b)
180
+ @ranking[a] > @ranking[b]
140
181
  end
141
182
 
142
183
  def rank_element(el)
@@ -180,7 +221,7 @@ module Vote
180
221
  end
181
222
 
182
223
  def check_limits(classifications, limit_results)
183
- raise TooManyClassificationsException if limit_results && classifications.size > limit_results
224
+ fail TooManyClassificationsException if limit_results && classifications.size > limit_results
184
225
  end
185
226
 
186
227
  def add_element(classifications, classif, _potential_winners, beated_list, start_list, element, limit_results)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schulze-vote
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.6
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alessandro Rodi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-12 00:00:00.000000000 Z
11
+ date: 2016-10-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: This gem is a Ruby implementation of the Schulze voting method (with
14
14
  help of the Floyd-Warshall algorithm), a type of the Condorcet voting methods.