forked from janos/schulze
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathschulze.go
166 lines (138 loc) · 3.37 KB
/
schulze.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Copyright (c) 2021, Janoš Guljaš <[email protected]>
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package schulze implements the Schulze method for single winner voting.
package schulze
import (
"sort"
)
// Score represents a total number of wins for a single choice.
type Score struct {
Choice string
Wins int
}
// VoteMatrix holds number of votes for every pair of choices.
type VoteMatrix map[string]map[string]int
// Compute calculates a sorted list of choices with the total number of wins for
// each of them. If there are multiple winners, tie boolean parameter is true.
func Compute(v VoteMatrix) (scores []Score, tie bool) {
choicesMap := make(map[string]struct{})
for c1, row := range v {
for c2 := range row {
choicesMap[c1] = struct{}{}
choicesMap[c2] = struct{}{}
}
}
size := len(choicesMap)
choices := make([]string, 0, size)
for c := range choicesMap {
choices = append(choices, c)
}
choiceIndexes := make(map[string]int)
for i, c := range choices {
choiceIndexes[c] = i
}
matrix := makeVoteCountMatrix(size)
for c1, row := range v {
for c2, count := range row {
matrix[choiceIndexes[c1]][choiceIndexes[c2]] = voteCount(count)
}
}
return compute(matrix, choices)
}
func compute(matrix [][]voteCount, choices []string) (scores []Score, tie bool) {
strengths := calculatePairwiseStrengths(matrix)
return calculateScores(strengths, choices)
}
func calculatePairwiseStrengths(m [][]voteCount) [][]strength {
size := len(m)
strengths := makeStrenghtMatrix(size)
for i := 0; i < size; i++ {
for j := 0; j < size; j++ {
if i != j {
c := m[i][j]
if c > m[j][i] {
strengths[i][j] = strength(c)
}
}
}
}
for i := 0; i < size; i++ {
for j := 0; j < size; j++ {
if i != j {
for k := 0; k < size; k++ {
if (i != k) && (j != k) {
strengths[j][k] = max(
strengths[j][k],
min(
strengths[j][i],
strengths[i][k],
),
)
}
}
}
}
}
return strengths
}
func calculateScores(strengths [][]strength, choices []string) (scores []Score, tie bool) {
size := len(strengths)
wins := make(map[int][]int)
for i := 0; i < size; i++ {
var count int
for j := 0; j < size; j++ {
if i != j {
if strengths[i][j] > strengths[j][i] {
count++
}
}
}
wins[count] = append(wins[count], i)
}
scores = make([]Score, 0, len(wins))
for count, choicesIndex := range wins {
for _, index := range choicesIndex {
scores = append(scores, Score{Choice: choices[index], Wins: count})
}
}
sort.Slice(scores, func(i, j int) bool {
if scores[i].Wins == scores[j].Wins {
return scores[i].Choice < scores[j].Choice
}
return scores[i].Wins > scores[j].Wins
})
if len(scores) >= 2 {
tie = scores[0].Wins == scores[1].Wins
}
return scores, tie
}
type voteCount int
type strength int
func makeVoteCountMatrix(size int) [][]voteCount {
matrix := make([][]voteCount, size)
for i := 0; i < size; i++ {
matrix[i] = make([]voteCount, size)
}
return matrix
}
func makeStrenghtMatrix(size int) [][]strength {
matrix := make([][]strength, size)
for i := 0; i < size; i++ {
matrix[i] = make([]strength, size)
}
return matrix
}
func min(a, b strength) strength {
if a < b {
return a
}
return b
}
func max(a, b strength) strength {
if a > b {
return a
}
return b
}