schulze-vote 2.0.6 → 2.1.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 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.