Advertisement
Guest User

Single Transferable Vote

a guest
Nov 11th, 2014
169
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 6.40 KB | None | 0 0
  1. /*
  2.  {
  3.   "posttype":"droop",
  4.   "numberofseats":"3",
  5.   "candidates" : ["Turtle","Gorilla","Monkey","Tiger","Leopard","Lynx"],
  6.   "votes":
  7.       [
  8.          ["Leopard","Lynx","Tiger","Monkey"],
  9.          ["Lynx","Leopard","Tiger"],
  10.          ["Lynx"],
  11.          ["Monkey","Tiger","Leopard"],
  12.          ["Tiger","Monkey","Gorilla","Leopard"],
  13.          ["Gorilla","Turtle","Monkey","Tiger"],
  14.          ["Turtle","Gorilla"],
  15.          ["Monkey","Gorilla","Tiger"],
  16.          ["Monkey","Gorilla","Leopard"],
  17.          ["Monkey","Leopard","Tiger"],
  18.          ["Monkey","Leopard","Gorilla"],
  19.          ["Monkey","Lynx","Turtle"],
  20.          ["Monkey","Turtle","Tiger"],
  21.          ["Monkey","Leopard","Tiger"],
  22.          ["Monkey","Gorilla","Tiger"],
  23.          ["Monkey","Gorilla","Tiger"],
  24.          ["Monkey","Turtle","Tiger"],
  25.          ["Monkey","Turtle","Tiger"],
  26.          ["Monkey","Lynx","Tiger"],
  27.          ["Tiger","Monkey","Gorilla"],
  28.          ["Gorilla","Turtle","Monkey","Tiger","Leopard"],
  29.          ["Gorilla","Turtle","Monkey","Tiger","Leopard"],
  30.          ["Gorilla","Turtle","Monkey","Tiger","Leopard"],
  31.          ["Gorilla","Turtle","Monkey","Tiger","Leopard"],
  32.          ["Gorilla","Turtle","Monkey","Tiger","Leopard"],
  33.          ["Gorilla","Turtle","Monkey","Tiger","Leopard"]
  34.       ]
  35.  }
  36.  */
  37.  
  38. using Newtonsoft.Json;
  39. using System;
  40. using System.Collections.Generic;
  41. using System.IO;
  42. using System.Linq;
  43. using System.Text;
  44. using System.Threading.Tasks;
  45.  
  46. namespace STV
  47. {
  48.     class JSONElectionInformation
  49.     {
  50.         public string posttype { get; set; }
  51.         public uint numberofseats { get; set; }
  52.         public List<string> candidates { get; set; }
  53.         public List<List<string>> votes { get; set; }
  54.     }
  55.     class Vote
  56.     {
  57.         public Vote(List<string> candidates,ICollection<string> votes)
  58.         {
  59.             Weight = 1m;
  60.             IEnumerable<uint> indexlist = from i in votes select (uint)candidates.IndexOf(i);
  61.             this.Ranks = new List<uint>(indexlist);
  62.         }
  63.  
  64.         public decimal Weight { get; set; }
  65.         public List<uint> Ranks { get; set; }
  66.  
  67.         public String ToString()
  68.         {
  69.             return String.Format("[{0} -> {1}]", Weight, String.Join(",", Ranks));
  70.         }
  71.  
  72.  
  73.     }
  74.     class Program
  75.     {
  76.         static void Main(string[] args)
  77.         {
  78.             string input = File.ReadAllText(args[0]);
  79.            
  80.             // Grab all my info from the specified JSON file
  81.             JSONElectionInformation fileinfo = JsonConvert.DeserializeObject<JSONElectionInformation>(input);
  82.             List<Vote> votes = (from i in fileinfo.votes select new Vote(fileinfo.candidates, i)).ToList();
  83.            
  84.             uint seatsrequired = fileinfo.numberofseats;
  85.             // check to see if we specified droop or hare method
  86.             decimal votesrequired = fileinfo.posttype == "droop" ?
  87.                 ((decimal)votes.Count / ((decimal)seatsrequired + 1)) + 1 : // droop (votes/(seats+1) + 1)
  88.                 ((decimal)votes.Count / ((decimal)seatsrequired));          // default is hare (votes/seats)
  89.  
  90.             List<uint> chosenseats = new List<uint>();
  91.             // while we still need to elect some people....
  92.             while (chosenseats.Count < seatsrequired)
  93.             {
  94.                
  95.                 // cull anyone who no longer votes for a candidate or whos vote carries no weight
  96.                 votes.RemoveAll(i => i.Ranks.Count == 0 || i.Weight == 0);
  97.                 // seperate all our votes into the dictionary based on who each persons (current) first choice is.
  98.                 Dictionary<uint, List<Vote>> splitvotes = new Dictionary<uint, List<Vote>>();
  99.                 foreach (Vote v in votes)
  100.                 {
  101.                     uint choice = v.Ranks.First();
  102.                     if (splitvotes.ContainsKey(choice))
  103.                         splitvotes[choice].Add(v);
  104.                     else
  105.                         splitvotes.Add(choice, new List<Vote> { v });
  106.                 }
  107.                 // if we have as many candidates as we have seats, we're done. Just elect them all.
  108.                 if (splitvotes.Count == (seatsrequired - chosenseats.Count))
  109.                 {
  110.                    
  111.                     foreach (uint i in splitvotes.Keys)
  112.                         chosenseats.Add(i);
  113.                     break;
  114.                 }
  115.                 // check for any people that meet or exceed the total number of votes needed
  116.                 List<uint> elected = (from i in splitvotes where i.Value.Sum(x => x.Weight) >= votesrequired select i.Key).ToList();
  117.                 if(elected.Count != 0){
  118.                     foreach (uint candidate in elected)
  119.                     {
  120.                         // proportionally redistribute excess votes - We're using the "everybody votes again at a fraction of a normal vote"
  121.                         // method rather than any random vote selection process
  122.                         decimal exceeded = splitvotes[candidate].Sum(x => x.Weight) - votesrequired;
  123.                         decimal multiplier = ((decimal)exceeded / (decimal)splitvotes[candidate].Count);
  124.                         foreach (Vote v in splitvotes[candidate])
  125.                             v.Weight *= multiplier;
  126.                        
  127.                         // since the candidates already elected, remove all references to them in the ranking.
  128.                         foreach (Vote v in votes)
  129.                             v.Ranks.RemoveAll(i => i == candidate);
  130.  
  131.                         chosenseats.Add(candidate);
  132.                     }
  133.                 }
  134.                 // If we have more candidates with votes than we have seats, we need to eliminate someone.
  135.                 else if (splitvotes.Count > (seatsrequired - chosenseats.Count))
  136.                 {
  137.                     // TODO: what to do if two people tie in total votes? Randomly choose one of them?
  138.                     uint loser = splitvotes.OrderBy(x => x.Value.Sum( vote => vote.Weight)).First().Key;
  139.                     // eliminate the loser - remove all references to them in the ranking
  140.                     foreach (Vote v in votes)
  141.                         v.Ranks.RemoveAll(i => i == loser);
  142.                        
  143.                 }
  144.                 else
  145.                 {
  146.                     // TODO: What happens if no one wants more people? Not sure...
  147.                     break;
  148.                 }
  149.             }
  150.             Console.WriteLine(String.Join(",", from i in chosenseats select fileinfo.candidates[(int)i]));
  151.             Console.ReadLine();
  152.         }
  153.     }
  154. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement