Python自定义列表构建优化

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (102)

我正在使用Python编程遗传算法,但是,我的运算符(MMX)需要太长时间(10秒)来执行具有300万个权重的个体(每个个体是3.000.000个元素的列表)。

这是运营商的代码:

def calc_gen(maxel,minel, rec1, rec2, phiC):
    g = maxel - minel
    phi = 0
    if g > phiC:
        # Recta 2
        phi = rec2[0] * g + rec2[1]
    elif g < phiC:
        # Recta 1
        phi = rec1[0] * g + rec1[1]
    #Hay que asegurarse que no nos salimos del rango:
    maxv = min(1, maxel - phi)
    minv = max(0, minel + phi)
    gen1 = random.uniform(minv, maxv)  # Guardar el gen del primer hijo
    # Si C es el centro y A el elemento que ya tenemos y B el simétrico de A: C - A + C = B -> 2C - A = B
    # C = (maxv + minv) / 2; 2C - A = B -> maxv + minv - A = B
    # center = (maxv + minv) / 2
    gen2 = maxv + minv - gen1
    return gen1, gen2
    #return gen1, maxv + minv - gen1

def cxMMX(poblacion, rec1, rec2, phiC):
    start = timer()
    # Calcular el maximo y el minimo de cada gen en toda la población
    max_genes = numpy.amax(poblacion, axis=0).tolist()
    min_genes = numpy.amin(poblacion, axis=0).tolist()
    gis = timer()
    hijo1 = Individual()
    hijo2 = Individual()
    # Iterar dos listas a la vez (zip) con su indice (enumerate). Así crearemos los hijos simultáneamente en un loop
    for i, (maxel, minel) in enumerate(zip(max_genes, min_genes)):
        gen1, gen2 = calc_gen(maxel, minel, rec1, rec2, phiC)
        hijo1.append(gen1)
        hijo2.append(gen2)
    end = timer()
    #print("Tiempo Gi: %f Tiempo init: %f Tiempo calc gen: %f Tiempo mate total: %f" % (gis-start, init-gis, end-init, end-start))
    return [hijo1, hijo2]

rec1,rec2和phiC是确定交叉如何完成的参数,你不应该为此烦恼。它们在整个算法中具有相同的值。

poblacion是一个列表列表,让我们说它的形状是[7,3000000]。Individual()是一个自定义类。它是bassicaly继承“列表”并添加一些属性来存储适合度值。

分别做numpy.amax和numpy.amin似乎做了额外的工作。此外,可能还有更多的pythonic方法来执行“calc_gen()”循环。

PD:“gen1”取决于“gen2”:gen1在一定范围内随机获得,然后获得gen2以寻找simetrical点。

PD2:有关MMX运算符的更详细说明可以在原始论文中找到 ,但是,您可以假设代码是okey并完成它必须做的事情。doi是https://doi.org/10.1007/3-540-44522-6_73

PD:enumerate()和我在旧代码中存在,忘了删除它们!

编辑:使用Dillon Davis的解决方案减少20%的时间。它是一个非常干净的解决方案,可以使用任何自定义列表构建功能,只要您通过执行一个函数获取列表的每个值:

def calc_gen_v2(maxel,minel, rec1m, rec1b, rec2m, rec2b, phiC):
    g = maxel - minel
    phi = 0
    if g > phiC:
        # Recta 2
        phi = rec2m * g + rec2b
    elif g < phiC:
        # Recta 1
        phi = rec1m * g + rec1b
    #Hay que asegurarse que no nos salimos del rango:
    maxv = min(1, maxel - phi)
    minv = max(0, minel + phi)
    gen1 = random.uniform(minv, maxv)  # Guardar el gen del primer hijo
    # Si C es el centro y A el elemento que ya tenemos y B el simétrico de A: C - A + C = B -> 2C - A = B
    # C = (maxv + minv) / 2; 2C - A = B -> maxv + minv - A = B
    # center = (maxv + minv) / 2
    gen2 = maxv + minv - gen1
    return gen1, gen2

def cxMMX_v3(poblacion, rec1, rec2, phiC):
    start = timer()
    # Calcular el maximo y el minimo de cada gen en toda la población
    max_genes = numpy.amax(poblacion, axis=0)
    min_genes = numpy.amin(poblacion, axis=0)
    gis = timer()
    hijo1, hijo2 = map(Individual, numpy.vectorize(calc_gen_v2)(max_genes, min_genes, rec1[0], rec1[1], rec2[0], rec2[1], phiC))
    end = timer()
    #print("Tiempo Gi: %f Tiempo init: %f Tiempo calc gen: %f Tiempo mate total: %f" % (gis-start, init-gis, end-init, end-start))
    return [hijo1, hijo2]

编辑2:正如Dillon Davis建议我在纯粹的numpy中实现它,将时间缩短到3.5秒!(节省65%的时间)

def cxMMX_numpy(poblacion, rec1, rec2, phiC):
    # Calculate max and min for every gen in the population
    max_genes = numpy.amax(poblacion, axis=0)
    min_genes = numpy.amin(poblacion, axis=0)
    g_pop = numpy.subtract(max_genes, min_genes)
    phi_pop = numpy.multiply(g_pop, rec1[0]) + rec1[1]
    maxv = numpy.minimum(numpy.subtract(max_genes, phi_pop), 1)
    minv = numpy.maximum(numpy.sum([max_genes, phi_pop], axis=0), 0)
    hijo1 = numpy.random.uniform(low=minv, high=maxv, size=minv.size)
    hijo2 = numpy.subtract(numpy.sum([maxv, minv], axis=0), hijo1)
    return [Individual(hijo1), Individual(hijo2)]

注意:如果要重用,Individual将从列表继承

然而,我不知道该怎么做: 如果你看一下代码,“phi”的微积分现在是一个线性的,而不是两个线性方程。我不知道如何进行g <phiC与numpy的比较。有帮助吗?

phi_pop = numpy.multiply(g_pop, rec1[0]) + rec1[1]

它应检查g_pop [i] <= phiC然后phi [i] = rec1 [0] * g_pop + rec1 1,否则phi [i] = rec2 [0] * g_pop + rec2 1

注意:当g = phiC rec1 [0] * g_pop + rec1 1 = 0时,总之,rec1 [0]和rec1 1保证!所以,如果有其他人,也许最好做一个三联?

提问于
用户回答回答于

你试过用过multiprocessing.Pool吗?

你需要首先制作一个包装器calc_gen

# after calc_gen def
def get_calc_gen(rec1, rec2, phiC):
    return lambda maxel, minel: calc_gen(maxel, minel, rec1, rec2, phiC)

然后代替for循环你会做类似的事情:

# replacing for loop section
cgen = get_calc_gen(rec1, rec2, phiC)
minmax_genes = zip(max_genes, min_genes)
pool = multiprocessing.Pool()
mapped_genes = pool.map(cgen, minmax_genes)
for gen1, gen2 in mapped_genes:
    hijo1.append(gen1)
    hijo2.append(gen2)

PS您enumerate原始代码中不需要,因为您似乎没有使用i

用户回答回答于

尝试用以下内容替换你的for循环cxMMX()

hijo1, hijo2 = map(Individual, numpy.vectorize(calc_gen)(max_genes, min_genes, rec1, rec2, phiC))

并放弃.tolist()你的numpy.amin()numpy.amax()

这将向你的calc_gen函数进行矢量化,避免使用zip和几次.append()调用的函数开销,并且整体上要快得多。

编辑:

还要考虑转换calc_gen()为直接在numpy数组上工作。调用替换random.uniform()numpy.random.uniform()min()max()numpy.minimum()numpy.maximum(),然后消除了环路/图+矢量化完全。这最终将是最快的选择。

扫码关注云+社区

领取腾讯云代金券