From 5dd50561fe0ac5a656ff75e1ee716abdca3a3331 Mon Sep 17 00:00:00 2001 From: Janos Guljas Date: Sat, 20 Jan 2024 20:44:19 +0100 Subject: [PATCH] Add Strength and Advantage to the Result --- schulze.go | 42 ++++++++++++++++++++----- schulze_test.go | 84 ++++++++++++++++++++++++------------------------- 2 files changed, 76 insertions(+), 50 deletions(-) diff --git a/schulze.go b/schulze.go index 3273d4f..aafc70d 100644 --- a/schulze.go +++ b/schulze.go @@ -188,6 +188,18 @@ type Result[C comparable] struct { Index int // Number of wins in pairwise comparisons to other choices votings. Wins int + // Total number of votes in wins in pairwise comparisons to other choices + // votings. + // Strength does not effect the winner, and may be less then the + // Strength of the choice with more wins. + Strength int + // Total number of preferred votes (difference between votes of the winner + // choice and the opponent choice) in wins in pairwise comparisons to other + // choices votings. Advantage does not effect the winner, and may be less + // then the Advantage of the choice with more wins. The code with less wins + // and greater Advantage had stronger but fewer wins and that information + // can be taken into the analysis of the results. + Advantage int } // Compute calculates a sorted list of choices with the total number of wins for @@ -324,24 +336,38 @@ func calculateResults[C comparable](choices []C, strengths []int) (results []Res results = make([]Result[C], 0, choicesCount) for i := 0; i < choicesCount; i++ { - var count int + var wins int + var popularity int + var advantage int for j := 0; j < choicesCount; j++ { if i != j { - if strengths[i*choicesCount+j] > strengths[j*choicesCount+i] { - count++ + sij := strengths[i*choicesCount+j] + sji := strengths[j*choicesCount+i] + if sij > sji { + wins++ + popularity += sij + advantage += sij - sji } } } - results = append(results, Result[C]{Choice: choices[i], Index: i, Wins: count}) - + results = append(results, Result[C]{ + Choice: choices[i], + Index: i, + Wins: wins, + Strength: popularity, + Advantage: advantage, + }) } sort.Slice(results, func(i, j int) bool { - if results[i].Wins == results[j].Wins { - return results[i].Index < results[j].Index + if results[i].Wins != results[j].Wins { + return results[i].Wins > results[j].Wins + } + if results[i].Strength != results[j].Strength { + return results[i].Strength > results[j].Strength } - return results[i].Wins > results[j].Wins + return results[i].Index < results[j].Index }) if len(results) >= 2 { diff --git a/schulze_test.go b/schulze_test.go index 7270145..304be40 100644 --- a/schulze_test.go +++ b/schulze_test.go @@ -38,7 +38,7 @@ func TestVoting(t *testing.T) { name: "single option no votes", choices: []string{"A"}, result: []schulze.Result[string]{ - {Choice: "A", Index: 0, Wins: 0}, + {Choice: "A", Index: 0, Wins: 0, Strength: 0, Advantage: 0}, }, }, { @@ -48,7 +48,7 @@ func TestVoting(t *testing.T) { {vote: schulze.Ballot[string]{"A": 1}}, }, result: []schulze.Result[string]{ - {Choice: "A", Index: 0, Wins: 0}, + {Choice: "A", Index: 0, Wins: 0, Strength: 0, Advantage: 0}, }, }, { @@ -58,8 +58,8 @@ func TestVoting(t *testing.T) { {vote: schulze.Ballot[string]{"A": 1}}, }, result: []schulze.Result[string]{ - {Choice: "A", Index: 0, Wins: 1}, - {Choice: "B", Index: 1, Wins: 0}, + {Choice: "A", Index: 0, Wins: 1, Strength: 1, Advantage: 1}, + {Choice: "B", Index: 1, Wins: 0, Strength: 0, Advantage: 0}, }, }, { @@ -70,8 +70,8 @@ func TestVoting(t *testing.T) { {vote: schulze.Ballot[string]{"A": 1, "B": 2}}, }, result: []schulze.Result[string]{ - {Choice: "A", Index: 0, Wins: 1}, - {Choice: "B", Index: 1, Wins: 0}, + {Choice: "A", Index: 0, Wins: 1, Strength: 2, Advantage: 2}, + {Choice: "B", Index: 1, Wins: 0, Strength: 0, Advantage: 0}, }, }, { @@ -83,9 +83,9 @@ func TestVoting(t *testing.T) { {vote: schulze.Ballot[string]{"A": 1, "B": 2, "C": 3}}, }, result: []schulze.Result[string]{ - {Choice: "A", Index: 0, Wins: 2}, - {Choice: "B", Index: 1, Wins: 1}, - {Choice: "C", Index: 2, Wins: 0}, + {Choice: "A", Index: 0, Wins: 2, Strength: 6, Advantage: 6}, + {Choice: "B", Index: 1, Wins: 1, Strength: 2, Advantage: 2}, + {Choice: "C", Index: 2, Wins: 0, Strength: 0, Advantage: 0}, }, }, { @@ -96,9 +96,9 @@ func TestVoting(t *testing.T) { {vote: schulze.Ballot[string]{"B": 1}}, }, result: []schulze.Result[string]{ - {Choice: "A", Index: 0, Wins: 1}, - {Choice: "B", Index: 1, Wins: 1}, - {Choice: "C", Index: 2, Wins: 0}, + {Choice: "A", Index: 0, Wins: 1, Strength: 1, Advantage: 1}, + {Choice: "B", Index: 1, Wins: 1, Strength: 1, Advantage: 1}, + {Choice: "C", Index: 2, Wins: 0, Strength: 0, Advantage: 0}, }, tie: true, }, @@ -112,11 +112,11 @@ func TestVoting(t *testing.T) { {vote: schulze.Ballot[string]{"A": 1, "B": 200, "C": 10}}, }, result: []schulze.Result[string]{ - {Choice: "A", Index: 0, Wins: 4}, - {Choice: "B", Index: 1, Wins: 2}, - {Choice: "C", Index: 2, Wins: 2}, - {Choice: "D", Index: 3, Wins: 0}, - {Choice: "E", Index: 4, Wins: 0}, + {Choice: "A", Index: 0, Wins: 4, Strength: 13, Advantage: 13}, + {Choice: "B", Index: 1, Wins: 2, Strength: 8, Advantage: 8}, + {Choice: "C", Index: 2, Wins: 2, Strength: 6, Advantage: 6}, + {Choice: "D", Index: 3, Wins: 0, Strength: 0, Advantage: 0}, + {Choice: "E", Index: 4, Wins: 0, Strength: 0, Advantage: 0}, }, }, { @@ -129,11 +129,11 @@ func TestVoting(t *testing.T) { {vote: schulze.Ballot[string]{"A": 1, "B": 200, "C": 10}}, }, result: []schulze.Result[string]{ - {Choice: "A", Index: 0, Wins: 4}, - {Choice: "B", Index: 1, Wins: 2}, - {Choice: "C", Index: 2, Wins: 2}, - {Choice: "C", Index: 3, Wins: 0}, - {Choice: "C", Index: 4, Wins: 0}, + {Choice: "A", Index: 0, Wins: 4, Strength: 13, Advantage: 13}, + {Choice: "B", Index: 1, Wins: 2, Strength: 8, Advantage: 8}, + {Choice: "C", Index: 2, Wins: 2, Strength: 6, Advantage: 6}, + {Choice: "C", Index: 3, Wins: 0, Strength: 0, Advantage: 0}, + {Choice: "C", Index: 4, Wins: 0, Strength: 0, Advantage: 0}, }, }, { @@ -194,11 +194,11 @@ func TestVoting(t *testing.T) { {vote: schulze.Ballot[string]{"E": 1, "B": 2, "A": 3, "D": 4, "C": 5}}, }, result: []schulze.Result[string]{ - {Choice: "E", Index: 4, Wins: 4}, - {Choice: "A", Index: 0, Wins: 3}, - {Choice: "C", Index: 2, Wins: 2}, - {Choice: "B", Index: 1, Wins: 1}, - {Choice: "D", Index: 3, Wins: 0}, + {Choice: "E", Index: 4, Wins: 4, Strength: 112, Advantage: 16}, + {Choice: "A", Index: 0, Wins: 3, Strength: 86, Advantage: 11}, + {Choice: "C", Index: 2, Wins: 2, Strength: 58, Advantage: 2}, + {Choice: "B", Index: 1, Wins: 1, Strength: 33, Advantage: 5}, + {Choice: "D", Index: 3, Wins: 0, Strength: 0, Advantage: 0}, }, }, { @@ -209,7 +209,7 @@ func TestVoting(t *testing.T) { {unvote: schulze.Record[string]{{"A"}}}, }, result: []schulze.Result[string]{ - {Choice: "A", Index: 0, Wins: 0}, + {Choice: "A", Index: 0, Wins: 0, Strength: 0, Advantage: 0}, }, }, { @@ -220,8 +220,8 @@ func TestVoting(t *testing.T) { {unvote: schulze.Record[string]{{"A"}, {"B"}}}, }, result: []schulze.Result[string]{ - {Choice: "A", Index: 0, Wins: 0}, - {Choice: "B", Index: 1, Wins: 0}, + {Choice: "A", Index: 0, Wins: 0, Strength: 0, Advantage: 0}, + {Choice: "B", Index: 1, Wins: 0, Strength: 0, Advantage: 0}, }, tie: true, }, @@ -233,8 +233,8 @@ func TestVoting(t *testing.T) { {unvote: schulze.Record[string]{}}, }, result: []schulze.Result[string]{ - {Choice: "A", Index: 0, Wins: 1}, - {Choice: "B", Index: 1, Wins: 0}, + {Choice: "A", Index: 0, Wins: 1, Strength: 1, Advantage: 1}, + {Choice: "B", Index: 1, Wins: 0, Strength: 0, Advantage: 0}, }, }, { @@ -248,11 +248,11 @@ func TestVoting(t *testing.T) { {unvote: schulze.Record[string]{{"A"}, {"B", "C"}, {"D", "E"}}}, }, result: []schulze.Result[string]{ - {Choice: "A", Index: 0, Wins: 3}, - {Choice: "B", Index: 1, Wins: 2}, - {Choice: "C", Index: 2, Wins: 2}, - {Choice: "D", Index: 3, Wins: 0}, - {Choice: "E", Index: 4, Wins: 0}, + {Choice: "A", Index: 0, Wins: 3, Strength: 8, Advantage: 8}, + {Choice: "B", Index: 1, Wins: 2, Strength: 6, Advantage: 6}, + {Choice: "C", Index: 2, Wins: 2, Strength: 4, Advantage: 4}, + {Choice: "D", Index: 3, Wins: 0, Strength: 0, Advantage: 0}, + {Choice: "E", Index: 4, Wins: 0, Strength: 0, Advantage: 0}, }, }, { @@ -268,11 +268,11 @@ func TestVoting(t *testing.T) { {unvote: schulze.Record[string]{{"B", "C"}, {"A"}, {"D", "E"}}}, }, result: []schulze.Result[string]{ - {Choice: "A", Index: 0, Wins: 4}, - {Choice: "C", Index: 2, Wins: 3}, - {Choice: "B", Index: 1, Wins: 2}, - {Choice: "D", Index: 3, Wins: 0}, - {Choice: "E", Index: 4, Wins: 0}, + {Choice: "A", Index: 0, Wins: 4, Strength: 4, Advantage: 4}, + {Choice: "C", Index: 2, Wins: 3, Strength: 3, Advantage: 3}, + {Choice: "B", Index: 1, Wins: 2, Strength: 2, Advantage: 2}, + {Choice: "D", Index: 3, Wins: 0, Strength: 0, Advantage: 0}, + {Choice: "E", Index: 4, Wins: 0, Strength: 0, Advantage: 0}, }, }, } {