Problem
Basically, there is an election on 3 regions where either candidate needs to win 2 in order to win the election. Idea is to simulate 10000 elections to see which candidate wins. I think my solution works but I am not happy with the huge list of if
statements and wondering of if there is a better way for this.
# Assignment simulate elections page 257
# 2 Candidates A and B
# Candidate A has following odds:
# 87% change of winning in region 1
# 65% changes of winning in region 2
# 17% changes of winning in region 3
from random import random
candidate_a_won = 0
candidate_b_won = 0
for i in range(0, 10000):
result = random()
candidate_a = 0
candidate_b = 0
if result + .87 >= 1:
candidate_a += 1
else:
candidate_b += 1
if result + .65 >= 1:
candidate_a += 1
else:
candidate_b += 1
if result + .17 >= 1:
candidate_a += 1
else:
candidate_b += 1
if candidate_a > candidate_b:
candidate_a_won += 1
else:
candidate_b_won += 1
print('Candidate A won elections {} times, candidate B won elections {} times'.format(candidate_a_won, candidate_b_won))
Solution

Your code has a problem. Candidate A can loose region 1, but win region 3.
Where with your code they can only win region 3 if they win region 2 and 1.
This is as they are independent, and so you shouldrandom
in each check. 
You want to move your chances outside the loop into an array.

You can simplify your
if
s into one comprehension. 
Candidate a wins if they have more than half the regions, you don’t need to calculate the amount of regions candidate b wins.
And so you could change your code to the following:
from random import random
AMOUNT = 10000
region_chances = [87, 65, 17]
region_chances = [1  n / 100 for n in region_chances]
regions = len(region_chances)
candidate_a_won = sum(
sum(random() >= chance for chance in region_chances) * 2 > regions
for _ in range(AMOUNT)
)
candidate_b_won = AMOUNT  candidate_a_won
print('Candidate A won elections {} times, candidate B won elections {} times'.format(candidate_a_won, candidate_b_won))
If you want to extend on the above then it’ll be hard if you decide to have more than two candidates, or even amounts of states.
But as this is a challenge, I don’t think you need to worry about these situations.
A slightly alternative approach is determining the chances for A to win beforehand, and then just do one election (not one per region).
The chances for A to win is the chance to win in region A and B, but not in region C; plus the chance of winning in region B and C but not A; …; plus the chance to win in all three regions:
AB(1−C)+A(1−B)C+(1−A)BC+ABC=AB+AC+BC−2ABC≈0.63$AB(1C)+A(1B)C+(1A)BC+ABC=AB+AC+BC2ABC\approx 0.63$
with A=0.87,B=0.65,C=0.17$A=0.87,B=0.65,C=0.17$
With this it becomes a bit easier:
amount = 10000
a, b, c = 0.87, 0.65, 0.17
chance = a*b + a*c + b*c  2*a*b*c
candidate_a_won = sum(random() < chance for _ in range(amount))
candidate_b_won = amount  candidate_a_won
print('Candidate A won elections {} times, candidate B won elections {} times'.format(candidate_a_won, candidate_b_won))
This is a bit faster, but not as easily extendable to N$N$ regions. For this we would need to derive a more general formula.
Per Graipher’s answer, I extended the chance of victory to accept an arbitrary number of regions and number of wins required.
from itertools import combinations
from operator import mul
def get_probability_of_victory(regs, required_wins):
prob = 0.
for wins in range(required_wins, len(regs) + 1):
for comb in combinations(regs, wins):
prob += reduce(mul, (regs[r] if r in comb else 1  regs[r] for r in regs))
return prob
regions = {'1': 0.87, '2': 0.65, '3': 0.17}
required_wins = 2
chance = get_probability_of_victory(regions, required_wins)
# continue with Graipher's code...