Guest User

Untitled

a guest
Jun 18th, 2018
67
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.28 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. import csv
  3. from math import floor
  4. import sys
  5.  
  6.  
  7. if len(sys.argv) < 2:
  8. print('Usage: python stv.py [ballot filename]')
  9. exit()
  10.  
  11. seats = 5
  12.  
  13.  
  14. class candidate:
  15. def __init__(self, name):
  16. self.name = name
  17. self.eligible = True
  18. self.elected = False
  19. self.score = 0.
  20.  
  21. def __lt__(self, other):
  22. return self.score < other.score
  23.  
  24.  
  25. class voter:
  26. def __init__(self, votes):
  27. self.votes = votes
  28. self.used = 0
  29. self.value = 1.
  30.  
  31.  
  32. with open(sys.argv[1], 'r') as f:
  33. reader = csv.reader(f)
  34. candidates = list(map(candidate, next(reader)))
  35.  
  36. votes = []
  37.  
  38. for row in reader:
  39. prefs = list(zip(row, candidates))
  40. prefs.sort(key=lambda t: t[0])
  41. votes.append(voter([t[1] for t in prefs if t[0] != '']))
  42.  
  43. threshold = floor(1 + len(votes) / (seats + 1))
  44.  
  45. print(f'{len(candidates)} candidates:')
  46. for c in candidates:
  47. print(f'- {c.name}')
  48. print()
  49. print(f'{seats} seats')
  50. print(f'{len(votes)} votes')
  51. print()
  52. print(f'Threshold: {threshold:.2f}')
  53. print()
  54.  
  55.  
  56. def ended():
  57. return len([c for c in candidates if c.eligible]) == seats or \
  58. len([c for c in candidates if c.score >= threshold]) == seats
  59.  
  60.  
  61. def show_scores():
  62. for c in candidates:
  63. if not c.eligible:
  64. continue
  65.  
  66. status = ''
  67.  
  68. if c.score > threshold and not c.elected:
  69. status = f'(elected, surplus: {(c.score - threshold):.2f})'
  70. if c.score == threshold or c.elected:
  71. status = '(elected)'
  72.  
  73. print(f'{c.name:<15}{c.score:<8.2f}{status}')
  74.  
  75.  
  76. def redistribute(c, value_factor=1.):
  77. for v in votes:
  78. if v.used < len(v.votes) and v.votes[v.used] == c:
  79. while v.used < len(v.votes) and \
  80. (v.votes[v.used].score >= threshold or
  81. not v.votes[v.used].eligible or v.votes[v.used] == c):
  82. v.used += 1
  83.  
  84. if v.used < len(v.votes):
  85. v.value *= value_factor
  86. v.votes[v.used].score += v.value
  87.  
  88.  
  89. round = 0
  90.  
  91. while not ended():
  92. print(f'Round {round + 1}')
  93. print()
  94.  
  95. n = False
  96.  
  97. for c in candidates:
  98. if c.score >= threshold and not c.elected:
  99. n = True
  100. c.elected = True
  101. if c.score > threshold:
  102. surplus_value = float(c.score - threshold) / c.score
  103. print(f'{c.name} has {(c.score - threshold):.2f} surplus votes, '
  104. f'which will be redistributed with a value of '
  105. f'{surplus_value:.2f} per vote.')
  106. redistribute(c, surplus_value)
  107.  
  108. if round == 0:
  109. print('Adding most preferred votes')
  110. for v in votes:
  111. v.votes[0].score += 1.
  112. elif not n:
  113. left = list(filter(lambda c: c.eligible, candidates))
  114. elim = filter(lambda c: c.score == min(left).score, left)
  115.  
  116. for c in elim:
  117. print(f'{c.name} has been eliminated')
  118. if c.score > 0:
  119. print(f'Their {c.score:.2f} votes will be redistributed.')
  120. c.eligible = False
  121. redistribute(c)
  122. c.score = 0.
  123.  
  124. print()
  125. show_scores()
  126. print()
  127. print()
  128.  
  129. round += 1
  130.  
  131. print('Winners are {}'.format(', '.join(c.name for c in filter(
  132. lambda c: c.eligible and c.score >= threshold, candidates))))
Add Comment
Please, Sign In to add comment