Nation Bulletin

[NON-RP] Making code for random elections

A few python codes I made using Thonny to simulate random elections quickly

By EuroNutella
03/26/2023 12:20 am
Updated: 03/26/2023 07:48 pm

  5
Share On:   

DISCLAIMER: I am still a learner and very new to Python. I am certain there are more efficient ways to make such programs but I found this to work for me and I wanted to share it, both for feedback and if anyone else was interested in using this little code.

 

Hi all! Recently I've been taking a university course which teaches me how to program rudimentary things using Python. Now, as my bulletins on the Monoj virus vaccine (shameless plug) may give away I'm a biotech student so the course really doesn't teach me how to program from 0 but just a few basics, hence the disclaimer at the top, and my only other experience with code was when trying to make a mod for HoI4 following a few tutorials. Anyways, it occured to me that what I've learned in the first few lessons is enough to make a little program to randomly generate percentages for my political parties. I'll share here the versions and my thought process.

 

Version 1:

For the first version of my program I wanted something that could generate random numbers that:

  • Summed up to 100%
  • Were never above 50%
  • Avoids decimals

So I came up with this:

#
# Election simulation
# by EuroNutella
# last updated: 24/03/2023
#
import random
PAN = 0
PFR = 0
PSU = 0
PCD = 0
SnD = 0
CDD = 0
FLU = 0
IR = 0
DH = 0
somma = 0

while somma != 100:
   PAN = random.randint(0,50)
   PFR = random.randint(0,50)
   PSU = random.randint(0,50)
   PCD = random.randint(0,50)
   SnD = random.randint(0,50)
   CDD = random.randint(0,50)
   FLU = random.randint(0,50)
   IR = random.randint(0,50)
   DH = random.randint(0,50)
   somma = (PAN + PFR + PSU + PCD + SnD + CDD + FLU + IR + DH)

print('PAN = ',PAN)
print('PFR = ',PFR)
print('PSU = ',PSU)
print('PCD = ',PCD)
print('SnD = ',SnD)
print('CDD = ',CDD)
print('FLU = ',FLU)
print('IR = ',IR)
print('DH = ',DH)
print('Somma: ',somma) #this is optional

Now, let me explain what I did. The first block simply imports the random functions and defines the variables, of course I used the acronyms of my parties but you can change it to yours and you can add or remove as many as you want by copy-pasting and changing the names (or if you're smarter than me you could use a python dictionary or something). The second block is the most important one: it begins with starting a while loop that repeats as long as "somma" (sum of all variables, as seen in the last string of the block) is different from 100; every variable will be turned into a random number that goes from 0 to 50 (this is because I didn't want any party to go above 50%, naturally you can change this) then summed; if somma is 100 then it stops and moves to the third bloc, which simply tells me the percentages.

You can of course change this if, for example, you want to assign number of seats instead of percentages you can change the 100 in "somma != 100" to whatever the maximum seats are in your parliament, or you can change the ranges at which each party is allowed to go to, say you want one party to never get more than 30 you'd change it to "random.randint(0,30)" or conversely you want it to always have more than 30% so "(30,100)" instead, the freedom is yours.

This code however has its drawbacks: it's inefficient. It takes between 2000 and 8000 ish attempts to find a combination that works for my specifications, and if you want each party to be able to win up to 100% of the vote it could take even more (you can see this by adding a counter that is increased by 1 for every loop). So in that scenario I asked ChatGPT for some codes (seeing as I didn't know how to make it more efficient), and these are what it came up with:

1st version:

# What ChatGPT proposed me
import random

parties = ['Party A', 'Party B', 'Party C', 'Party D', 'Party E', 'Party F', 'Party G', 'Party H', 'Party I']

total_percentage = 0
percentages = []

# Generate random percentages for each party
for i in range(len(parties)):
   max_percentage = 100 - total_percentage
   percentage = random.uniform(0, max_percentage)
   total_percentage += percentage
   percentages.append(percentage)

# Sort the percentages in descending order
percentages.sort(reverse=True)

# Print the results
for i in range(len(parties)):
   print(parties[i] + ': ' + str(percentages[i]) + '%')

This is probably a smarter way to do it but I found it tends to generate 1 huge party and many parties that are 0.0.....1%, also lots of decimals, so I tried asking it if it could make the parties' percentages go up to 50 at most but it misunderstood and insted made their sum go up to 50, so I just decided to change the sum so that it goes back to 100 and every party can go up to 100 in the 2nd version.

2nd version:

#second attempt
import random

parties = ['Party A', 'Party B', 'Party C', 'Party D', 'Party E', 'Party F', 'Party G', 'Party H', 'Party I']

total_percentage = 0
percentages = []

# Generate random percentages for each party
for i in range(len(parties)-1):
   max_percentage = min(50, 100 - total_percentage - (len(parties)-i-1)*1)
   percentage = random.uniform(0, max_percentage)
   total_percentage += percentage
   percentages.append(percentage)

# The last party gets the remaining percentage
percentages.append(100 - total_percentage)

# Shuffle the list of percentages
random.shuffle(percentages)

# Print the results
for i in range(len(parties)):
   print(parties[i] + ': ' + str(round(percentages[i], 2)) + '%')

This works much better to distribute it more evenly apparently, and only goes up to 2 decimals. If you just want a random percentage calculator that goes up to 100 with as many parties as you'd like this is probably the best option you have.

I also gave ChatGPT to make my own code more efficient and it came up with:

import random

values = [0] * 9 #9 being the number of parties

while sum(values) != 100:
   for i in range(len(values)):
       values[i] = random.randint(0, 50)

print('PAN = ', values[0])
print('PFR = ', values[1])
print('PSU = ', values[2])
print('PCD = ', values[3])
print('SnD = ', values[4])
print('CDD = ', values[5])
print('FLU = ', values[6])
print('IR = ', values[7])
print('DH = ', values[8])
print('Somma: ', sum(values))
#more efficient???

Tho in reality it's more readable sure but not more efficient in terms of how many tries it needs to find the percentages.

 

2nd version

It occured to me however that while it is great to have a random percentage generator for my RP I need something that takes into account how the previous election went, and have the party's popularity change based on that. So I came up with this:

#
# Election sim 2
# by EuroNutella
# last updated: 25/03/2023
#
import random
# Defines what the current percentages are
nPAN = input('What value does PAN have: ')
nPAN = int(nPAN)
nPFR = input('What value does PFR have: ')
nPFR = int(nPFR)
nPSU = input('What value does PSU have: ')
nPSU = int(nPSU)
nPCD = input('What value does PCD have: ')
nPCD = int(nPCD)
nSnD = input('What value does SnD have: ')
nSnD = int(nSnD)
nCDD = input('What value does CDD have: ')
nCDD = int(nCDD)
nFLU = input('What value does FLU have: ')
nFLU = int(nFLU)
nIR = input('What value does IR have: ')
nIR = int(nIR)
nDH = input('What value does DH have: ')
nDH = int(nDH)
# Needed
PAN = 0
PFR = 0
PSU = 0
PCD = 0
SnD = 0
CDD = 0
FLU = 0
IR = 0
DH = 0
somma = 0
# Calculates random percentages until they add up to 100 taking into account current percentages
while somma != 100:
   PAN = random.randint(max(0, nPAN-5), nPAN+5)
   PFR = random.randint(max(0, nPFR-5), nPFR+5)
   PSU = random.randint(max(0, nPSU-5), nPSU+5)
   PCD = random.randint(max(0, nPCD-5), nPCD+5)
   SnD = random.randint(max(0, nSnD-5), nSnD+5)
   CDD = random.randint(max(0, nCDD-5), nCDD+5)
   FLU = random.randint(max(0, nFLU-5), nFLU+5)
   IR = random.randint(max(0, nIR-5), nIR+5)
   DH = random.randint(max(0, nDH-5), nDH+5)
   somma = (PAN + PFR + PSU + PCD + SnD + CDD + FLU + IR + DH)
# Tells me the percentages and their sum to verify it works
print('PAN = ',PAN)
print('PFR = ',PFR)
print('PSU = ',PSU)
print('PCD = ',PCD)
print('SnD = ',SnD)
print('CDD = ',CDD)
print('FLU = ',FLU)
print('IR = ',IR)
print('DH = ',DH)
print('Somma: ',somma)

Now in this before calculating the percentages it asks what percentage the parties already have, then calculates new percentages that can either be 5 points less or 5 points more than the already existing percentage. Of course for parties that have less than 5% of the vote you also need to tell it that it can't go below 0, hence why instead of just nXX-5 (or whatever number you want it to change at most) I wrote "max(0,nXX-5)", this can also be used to set a maximum percentage like 50 if you want "max(nXX+5,50)". It also appears to be way more efficient, only requiring about 10 to 20 tries to provide more believable results (if yours is a democracy where people don't vote parties completely at random but some keep voting a certain party), this is mostly because it has a narrower range of numbers it can generate starting from numbers that should already add up to 100 (so you could use the 1st version to get some random numbers for the first time then switch to the second).

Of course the drawback here is that adding or removing parties becomes annoying, so I asked ChatGPT to fix the issue:

import random

# Define party names
parties = ['PAN', 'PFR', 'PSU', 'PCD', 'SnD', 'CDD', 'FLU', 'IR', 'DH']

# Get user input for each party's percentage
percentages = {}
for party in parties:
   while True:
       percentage = input(f"What value does {party} have: ")
       try:
           percentage = int(percentage)
           if percentage >= 0 and percentage <= 100:
               percentages[party] = percentage
               break
           else:
               print("Percentage must be between 0 and 100")
       except ValueError:
           print("Invalid input, please enter an integer")

# Generate random percentages until they add up to 100 taking into account current percentages
while True:
   random_percentages = {}
   for party in parties:
       min_percentage = max(0, percentages[party] - 5)
       max_percentage = min(100, percentages[party] + 5)
       random_percentages[party] = random.randint(min_percentage, max_percentage)
   if sum(random_percentages.values()) == 100:
       break

# Print the percentages and their sum
for party, percentage in random_percentages.items():
   print(f"{party} = {percentage}")
print(f"Sum: {sum(random_percentages.values())}")

Tho honestly I have little clue what it's doing looking at a first glance it seems to be working perfectly and it's probably easier to just add parties to the "parties = ['XXX', 'YYY', 'ZZZ']" than copy-pasting

Version 3

So far I've only had time to do these two versions but another potential idea could be to perhaps skip the loop and instead have it generate a random number of votes, say maybe add a max population variable, then have the random votes be summed, then divide each single party's vote by the total, multiply it by 100 and voila you have a random percentage, plus an turnout too if you divide the sum of the votes with the max population. I may do this tomorrow and edit this bulletin with the result.

 

So here's the much anticipated edit: I made a version based on what chatGPT suggested for version 1 in which it generates a random amount of votes based on a maximum population sample that you give to it, then it will sum the votes and divide each vote with the sum to get a percentage.

 

#
# Election Simulator 3
# by EuroNutella
# last updated 26/03/2023
#
import random

parties = ['PAN', 'PFR', 'PSU', 'PCD', 'SnD', 'CDD', 'FLU', 'IR', 'DH']
votes = []
total_votes = 0
max_pop = input('What is the population? ')
max_pop = int(max_pop)

for i in range(len(parties)-1):
   max_vote = max_pop - total_votes - (len(parties)-i-1)*1
   vote = random.randint(0,max_vote)
   total_votes += vote
   votes.append(vote)

votes.append(max_pop - total_votes)

random.shuffle(votes)

for i in range(len(parties)):
   print(parties[i] + ': ' + str(round(votes[i]/max_pop*100, 0)) + '%')

I also made a version that instead of always having 100% turnout has a random turnout too:

#
# Election Simulator 4
# by EuroNutella
# last updated 26/03/2023
#
import random

parties = ['PAN', 'PFR', 'PSU', 'PCD', 'SnD', 'CDD', 'FLU', 'IR', 'DH']
votes = []
total_votes = 0
max_pop = input('What is the population? ')
max_pop = int(max_pop)

for i in range(len(parties)):
   max_vote = max_pop - total_votes - (len(parties)-i)*1
   vote = random.randint(0,max_vote)
   total_votes += vote
   votes.append(vote)

random.shuffle(votes)

for i in range(len(parties)):
   print(parties[i] + ': ' + str(round(votes[i]/max_pop*100, 0)) + '%')
print('Turnout: ' + str(round(total_votes/max_pop*100, 0)) + '%')

Tho results tend to still have parties that have big percentages and parties with 0%

 

Feel free to copy & paste this into your preferred program (I used Thonny) to edit it and use it.

 

As for now, I'll experiment more before implementing this program for my own elections, and I await any useful feedback because again, even if I'm happy that my code works well for what I need it to do, I'm a total noob when it comes to programming (as is probably evident to anyone who uses python a lot) and I'm aware there's a lot to improve on. And hopefully more and better programs can see come out of this bulletin.

See ya!

Replies

Posted March 26, 2023 at 12:37 am

I hope you are successful in your endeavors, but is that going to be just a personal thing, or is it a thing that is avaliable to the public for usage? I'm asking because I also want to use something to make random elections.

  4
Posted March 26, 2023 at 12:55 am

same.     

  2
Posted March 26, 2023 at 1:25 am

You're free to copy and paste this into thonny or whatever program you use. I may link the files themselves tomorrow if you want.

  1