# I do not know why my simulation is so slow. I need to optimize my simulation and improve the way I use variables

This is a python program that does some sort of simulation,, I am looking for any type of optimization while keeping the same p[i] form, I have tried Pypy and I got an around 3x performance gain over python. Any suggestions are welcomed

```import random
from time import perf_counter
infected, non_infected = 1, 99999
infectation_chance, infection_days, death_chance = 1/100, 2/1000, 2/100
population, population_list = infected + non_infected, non_infected *  + infected * 
place = 10
p = {i: [] for i in range(1,place + 1)}
day = 1
simulation_duration = 3
while 0 < simulation_duration:
print(f"Working on day {day}..")
time1 = perf_counter()
for person in population_list:
p[random.randint(1, place)].append(person)
time2 = perf_counter()
i = 0
while i < place:
tl = []
i += 1
for crowd in p[i]:
if crowd == 0:
if (random.random() < infectation_chance * str(p[i]).count("1")) - (infectation_chance/100 * str(p[i]).count("0")):
tl.append(1)
else:
tl.append(0)
if crowd == 1:
tl.append(1)
p[i] = tl

i = 0
population_list = []
while i < place:
i += 1
population_list.append(p[i])
simulation_duration -= 1
day += 1
print(f"Total time: {perf_counter() - time1} nInfection time: {perf_counter() - time2} nPlacing time: {time2-time1}")
print(str(population_list).count("1"), str(population_list).count("0"))

```

Even tho I received lots of help I still need more optimization.Any type of optimization as far as it doesn’t change the results are welcomed.Since this is fully compatible with pypy I am using pypy, I can also use python if it means better performance. Current setup:

```import random
from time import perf_counter
infected, non_infected = 1000, 999000
infectation_chancea, death_chance, recovery_chance ,reinfectation_chance, incubation_time = 100/6, 1 - 0.03, 1 - 0.899, 1 - 2/100, 1
death_chance, recovery_chance = death_chance / incubation_time, recovery_chance/incubation_time
population, population_list = infected + non_infected, non_infected *  + infected * 
place = 2
day = 1
simulation_duration = 100000000
with open("results.txt", "w") as results:
results.seek(0)
results.write("")
results.close()
with open("results.txt", "a+") as results:
while infected > 0  and simulation_duration > 0:
population = population_list.count(0) + population_list.count(-1) + population_list.count(1)
healthy = population_list.count(0) + population_list.count(-1)
recovered = population_list.count(-1)
infected = population_list.count(1)
died = population_list.count(2)
p = {i: [] for i in range(1,place + 1)}
results.write(f"Day {day}: Infected: {infected} Healthy: {healthy} Recovered: {recovered} Alive: {population} Died: {died} n")
time1 = perf_counter()
for person in population_list:
p[random.randint(1, place)].append(person)
time2 = perf_counter()
i = 0
while i < place:
i += 1
pi_infected = p[i].count(1)
infectation_chance =  1 - pi_infected / float((float(len(p[i])) / infectation_chancea))
for j, crowd in enumerate(p[i]):
if crowd == 2:
pass
elif crowd == 0:
if random.random() > infectation_chance):
p[i][j] = 1

elif crowd == 1:
if random.random() > death_chance):
p[i][j] = 2

elif random.random() > recovery_chance):
p[i][j] = -1

elif crowd == -1:
if random.random() > reinfectation_chance):
p[i][j] = 1

i = 0
population_list = []
while i < place:
i += 1
population_list.extend(p[i])
simulation_duration -= 1
day += 1
print("Simulation finishsed, check 'results.txt' for the results)
```

here is a corrected version in pure python, commented because there were some bugs. your major time loss was counting infected/non-infected inside the for loop, though the result is always the same. it could be optimized again with numpy if you wanna use a bigger population

```import random
from time import perf_counter
infected, non_infected = 1, 99999
infectation_chance, infection_days, death_chance = 1/100, 2/1000, 2/100
population, population_list = infected + non_infected, non_infected *  + infected * 
place = 10
day = 1
simulation_duration = 3
while 0 < simulation_duration:
# p must be reset here or it will grow indefinitely
p = {i: [] for i in range(1,place + 1)}
print(f"Working on day {day}..")
time1 = perf_counter()
for person in population_list:
p[random.randint(1, place)].append(person)
time2 = perf_counter()
i = 0
while i < place:
i += 1
# if you need to, count infected/non-infected here
# not in your for loop where it has always the same value
# and don't cast to str, what's the point?
# pi_infected = p[i].count(1)
# pi_sane = p[i].count(0)
for j, crowd in enumerate(p[i]):
if crowd == 0:
# your formula was broken (a-b is always true)
# i used a generic one
if random.random()>(1-infectation_chance):
# change your list in place:
# no need for temp list, save lots of cycles
p[i][j] = 1

i = 0
population_list = []
while i < place:
i += 1
# it's extend, not append here or your population list
# will have a length of #place
population_list.extend(p[i])
simulation_duration -= 1
day += 1
print(f"Total time: {perf_counter() - time1} nInfection time: {perf_counter() - time2} nPlacing time: {time2-time1}")
print(population_list.count(1), population_list.count(0))
```

numpy version

```import random
import numpy as np
from time import perf_counter
infected, non_infected = 1, 99999
infectation_chance, infection_days, death_chance = 1/100, 2/1000, 2/100

place = 10
population = infected + non_infected
group_size = population//place

population_list=np.zeros((population))
population_list[:infected]=1

day = 1
simulation_duration = 3

while 0 < simulation_duration:
print(f"Working on day {day}..")
time1 = perf_counter()
# shuffle is not recursive so we need to flatten population_list
population_list=population_list.flatten()
np.random.shuffle(population_list)
population_list=population_list.reshape((place, group_size))
time2 = perf_counter()

# we need to rebuild the pure python code with no loops
# first we create randoms for all pop
randoms = np.random.rand(population).reshape((place, group_size))

# list of infected by group: a list of all p[i].count(1)
nb_infected = np.count_nonzero(population_list, axis=1).reshape((place,1))
# compute (1-infectation_chance**p[i].count(1)) for all pop
infection_map=np.full((place, group_size), 1-infectation_chance)**nb_infected
# if randoms>infection_map and population_list==0
new_infected = np.bitwise_and(randoms>infection_map, population_list==0)
# then set to 1 in place
population_list[new_infected] = 1

simulation_duration -= 1
day += 1
print(f"Total time: {perf_counter() - time1} nInfection time: {perf_counter() - time2} nPlacing time: {time2-time1}")
total_infected=np.count_nonzero(population_list)
print(total_infected, population-total_infected)
```