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 * [0] + infected * [1] 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 * [0] + infected * [1] 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: print("Starting... nPlease wait for 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)

## Answer

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 * [0] + infected * [1] 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)