我不知道为什么我的模拟这么慢。我需要优化我的模拟并改进我使用变量的方式
I do not know why my simulation is so slow. I need to optimize my simulation and improve the way I use variables
这是一个进行某种模拟的 python 程序,我正在寻找任何类型的优化,同时保持相同的 p[i] 形式,我已经尝试过 Pypy,我得到了大约 3 倍性能提升超过 python。欢迎任何建议
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"))
即使我收到了很多帮助,我仍然需要更多 optimization.Any 类型的优化,只要它不会改变结果 welcomed.Since 这与 pypy 完全兼容 我正在使用 pypy ,我也可以使用 python 如果它意味着更好的性能。当前设置:
import random
import functools
from time import perf_counter
with open("results.txt", "w") as results:
results.seek(0)
results.write("")
results.close()
time1 = perf_counter()
@functools.lru_cache(maxsize=128)
def simulation():
infected, non_infected = 1, 99999999
infectation_chance_c, death_chance, recovery_chance, reinfectation_chance, incubation_time = 1.4, 1 - 0.03, 1 - 0.97, 1 - 1 / 150, 2
death_chance, recovery_chance = death_chance / incubation_time, recovery_chance / incubation_time
population_total, population_list = infected + non_infected, non_infected * [0] + infected * [1]
place = 1
day = 0
simulation_duration = 100000000
with open("results.txt", "a") as results:
print("Starting... \nPlease wait for results, this can take lots of time!")
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_total - len(population_list)
p = {i: [] for i in range(1,place + 1)}
results.write(f"Day {day}: Infected: {infected} Healthy: {healthy} p-Imune: {recovered} Alive: {population} Died: {died} \n")
print(f"Day {day}: Infected: {infected} Healthy: {healthy} p-Imune: {recovered} Alive: {population} Died: {died}")
for person in population_list:
p[random.randint(1, place)].append(person)
i = 0
while i < place:
i += 1
p_infected = p[i].count(1)
try:
infectation_chance = 1 - float(p_infected) / (float(len(p[i])) / infectation_chance_c)
except:
pass
for j, crowd in enumerate(p[i]):
if crowd == -1:
if random.random() > reinfectation_chance:
p[i][j] = 1
elif random.random() > reinfectation_chance:
p[i][j] = 0
elif crowd:
if random.random() > death_chance:
p[i].pop(j)
elif random.random() > recovery_chance:
if random.random() > 0.4:
p[i][j] = -1
else:
p[i][j] = 0
elif not crowd:
if random.random()>infectation_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(f"Total time: {perf_counter() - time1}")
simulation()
print(f"Simulation finishsed... \nProcessing time: {perf_counter()-time1}")
这里是一个更正的纯 python 版本,因为有一些错误而被评论。你的主要时间损失是在 for 循环中计算 infected/non-infected,尽管结果总是一样的。如果你想使用更大的人口,它可以用 numpy 再次优化
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 版本
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)
这是一个进行某种模拟的 python 程序,我正在寻找任何类型的优化,同时保持相同的 p[i] 形式,我已经尝试过 Pypy,我得到了大约 3 倍性能提升超过 python。欢迎任何建议
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"))
即使我收到了很多帮助,我仍然需要更多 optimization.Any 类型的优化,只要它不会改变结果 welcomed.Since 这与 pypy 完全兼容 我正在使用 pypy ,我也可以使用 python 如果它意味着更好的性能。当前设置:
import random
import functools
from time import perf_counter
with open("results.txt", "w") as results:
results.seek(0)
results.write("")
results.close()
time1 = perf_counter()
@functools.lru_cache(maxsize=128)
def simulation():
infected, non_infected = 1, 99999999
infectation_chance_c, death_chance, recovery_chance, reinfectation_chance, incubation_time = 1.4, 1 - 0.03, 1 - 0.97, 1 - 1 / 150, 2
death_chance, recovery_chance = death_chance / incubation_time, recovery_chance / incubation_time
population_total, population_list = infected + non_infected, non_infected * [0] + infected * [1]
place = 1
day = 0
simulation_duration = 100000000
with open("results.txt", "a") as results:
print("Starting... \nPlease wait for results, this can take lots of time!")
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_total - len(population_list)
p = {i: [] for i in range(1,place + 1)}
results.write(f"Day {day}: Infected: {infected} Healthy: {healthy} p-Imune: {recovered} Alive: {population} Died: {died} \n")
print(f"Day {day}: Infected: {infected} Healthy: {healthy} p-Imune: {recovered} Alive: {population} Died: {died}")
for person in population_list:
p[random.randint(1, place)].append(person)
i = 0
while i < place:
i += 1
p_infected = p[i].count(1)
try:
infectation_chance = 1 - float(p_infected) / (float(len(p[i])) / infectation_chance_c)
except:
pass
for j, crowd in enumerate(p[i]):
if crowd == -1:
if random.random() > reinfectation_chance:
p[i][j] = 1
elif random.random() > reinfectation_chance:
p[i][j] = 0
elif crowd:
if random.random() > death_chance:
p[i].pop(j)
elif random.random() > recovery_chance:
if random.random() > 0.4:
p[i][j] = -1
else:
p[i][j] = 0
elif not crowd:
if random.random()>infectation_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(f"Total time: {perf_counter() - time1}")
simulation()
print(f"Simulation finishsed... \nProcessing time: {perf_counter()-time1}")
这里是一个更正的纯 python 版本,因为有一些错误而被评论。你的主要时间损失是在 for 循环中计算 infected/non-infected,尽管结果总是一样的。如果你想使用更大的人口,它可以用 numpy 再次优化
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 版本
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)