我不知道为什么我的模拟这么慢。我需要优化我的模拟并改进我使用变量的方式

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)