以指定索引对列表中的项求和的快速、python方法是什么?

以指定索引对列表中的项求和的快速、python方法是什么?,python,algorithm,Python,Algorithm,我必须把列表中的一部分项目汇总起来。我用来计算指数的数字在另一个列表中给出。索引可能会改变,但我从中求和的列表不会改变。两个列表的大小都是已知的。这段代码位于我的程序的嵌套循环中,是迄今为止最慢的部分 在Python3中,我需要一种快速而干净的方法来实现这一点 我尝试过的一个明显的解决方案就是硬编码所有不同项目的总和。我还尝试了一个更干净的解决方案,使用枚举和总结理解。问题是后者比前者慢得多 要使用的确切索引是2*i+x,其中i是索引中的索引,x是索引中的数字。索引列表表示连接到查找表中的值之间

我必须把列表中的一部分项目汇总起来。我用来计算指数的数字在另一个列表中给出。索引可能会改变,但我从中求和的列表不会改变。两个列表的大小都是已知的。这段代码位于我的程序的嵌套循环中,是迄今为止最慢的部分

在Python3中,我需要一种快速而干净的方法来实现这一点

我尝试过的一个明显的解决方案就是硬编码所有不同项目的总和。我还尝试了一个更干净的解决方案,使用枚举和总结理解。问题是后者比前者慢得多

要使用的确切索引是2*i+x,其中i是索引中的索引,x是索引中的数字。索引列表表示连接到查找表中的值之间的一组选择

示例代码-实际列表要大得多 查找=[7,10,1,4,1,7,9,3,5,6] 指数=[0,1,0,0,1] 硬编码解决方案 s=查阅[2*0+索引[0]]+查阅[2*1+索引[1]]+查阅[2*2+索引[2]]+查阅[2*3+索引[3]+查阅[2*4+索引[4]] 带枚举的Pythonic解决方案 s=枚举索引中i,x的sumlookup[2*i+x]
我已经使用测试工具测试了这两个选项。使用enumerate的干净解决方案比硬编码版本慢3倍多。其余的代码都经过了相当的优化,所以如果我使用干净的版本,我的整个程序几乎要慢3倍

我能做些更快的事吗?需要以某种方式对查找列表进行预处理的答案是可以的:该列表只构建了一次,但使用了很多次

编辑:下面是一个完整的示例,其中硬编码查找列表似乎比任何其他方法都快得多。以下代码在Py3中以0.27s的速度运行,注释掉的慢版本以2.8s的速度运行。显然,有更快的方法来完成这项任务

from itertools import product

lookup = [1, 7, 7, 1, 2, 9, 9, 9, 2, 2, 8, 8, 9, 6, 5, 10, 3, 4, 7, 10, 1, 3, 0, 1, 7, 1, 3, 4, 2, 9]
largest_sum = 0
largest_sum_indices = []

for indices in product(list(range(0,2)), repeat=15):
    # simulate checking many different lookup lists
    for _ in range(200):
        s = lookup[2 * 0 + indices[0]] + lookup[2 * 1 + indices[1]] + lookup[2 * 2 + indices[2]] + lookup[2 * 3 + indices[3]] + lookup[2 * 4 + indices[4]] + lookup[2 * 5 + indices[5]] + lookup[2 * 6 + indices[6]] + lookup[2 * 7 + indices[7]] + lookup[2 * 8 + indices[8]] + lookup[2 * 9 + indices[9]] + lookup[2 * 10 + indices[10]] + lookup[2 * 11 + indices[11]] + lookup[2 * 12 + indices[12]] + lookup[2 * 13 + indices[13]] + lookup[2 * 14 + indices[14]]
        # clean method is too slow
        #s = sum(lookup[i * 2 + x] for i,x in enumerate(indices))
        if s > largest_sum:
            largest_sum = s
            largest_sum_indices = indices

print(largest_sum)
print(largest_sum_indices)


这对numpy来说似乎是一项很好的任务。它允许您对正在执行的许多操作进行矢量化,在后台运行C实现

import numpy as np

lookup = np.array([7, 10, 1, 4, 1, 7, 9, 3, 5, 6], dtype=np.int)
indices = np.array([0, 1, 0, 0, 1], dtype=np.int)

real_indices = np.arange(0, 2 * indices.size, 2) + indices
s = lookup[real_indices].sum()
可以使用函数itemgetter进行快速查找:

from operator import itemgetter
from itertools import count

lookup = [7, 10, 1, 4, 1, 7, 9, 3, 5, 6]
indices = [0, 1, 0, 0, 1]

sum(itemgetter(*[i + j for i, j in zip(count(step=2), indices)])(lookup))
# 27

我认为你确实需要重新评估你的算法,而不是试图从无辜的代码中挤出周期,如果编辑是你真正想要做的

让我们从用文字描述代码的功能开始。您有一个大小为2N的查找数组。指定位0或1以指示将从每个连续对中选择哪个元素,并将N个选定元素相加。通过检查从0到2**N-1的每个位组合,您希望找到N个元素的最大和

我假设,如果您仍然需要,只需在一次过程中检查N对元素,就可以得到正确的总和和索引。您可以分N个步骤完成此操作,而不是N*2**N

这是一个非常基本的、完全未优化的解决方案,我打赌它比问题中的解决方案扩展得更好:

lookup = ...
N = len(lookup) // 2
largest_sum = 0
largest_sum_indices = [0] * N
for i in range(N):
    if lookup[2 * i + 1] > lookup[2 * i]:
        largest_sum_indices[i] = 1
        largest_sum += lookup[2 * i + 1]
    else:
        largest_sum += lookup[2 * i]
注意,除了len之外,我没有调用任何函数,只使用一个基本for循环

以下是一个优化的numpy版本:

import numpy as np

lookup = np.array(...)
largest_sum_indices = np.argmax(lookup.reshape(lookup.size // 2, 2), axis=1)
largest_sum = lookup[2 * np.arange(largest_sum_indices.size) + largest_sum_indices]

虽然您自己的测试会告诉您哪种算法最适合您,但请记住,这里的任一选项都可以轻松地处理数百万个元素,而不会让用户过于焦虑,而某些按*2**N缩放的对象所需的时间要比许多宇宙的热死时间更长。无论何时使用该产品,都有可能找到更好的解决方案。

您是否考虑过使用C?或者设置最小numpy?以绝对值计算,我们在这方面的速度快了多少?实际索引中有多少个元素?其余的代码都经过了相当的优化,所以如果使用干净的版本,我的整个程序几乎要慢3倍。我不相信这一点,除非你的程序有4行。你信不信由你,但这是真的。整个程序只是在一系列不同的索引上循环,执行一些非常小的计算,并存储结果。我刚刚检查过它-使用Py3和一些测试输入,慢版本需要2.07秒。硬编码版本需要0.7秒。两个程序之间的唯一区别是我的示例代码中的一行。这是一个实际的区别,b非常惊人。你能发布整个程序,至少拿出一个样本,我可以用它来测试差异吗?我现在真的很感兴趣。也许您可以利用查找的一致性,或者改变将索引驱动为原始索引的方式?无论哪种方式,我都想仔细研究一下。不幸的是,使用perf进行的测试表明,这比枚举版本还要慢。enumerate版本需要1.38 us才能运行,您的最后一行需要1.84 us。两者都比只需要0.41 us的硬编码版本差得多。@adamf尝试lambda x,y:x+y而不是add。这更糟糕。2.31美国。尝试[i+j代表i,zipcountstep=2中的j,索引]而不是map。是的,任何时候使用map都是基因
rally将比等效理解慢。不幸的是,这样做比enumerate还要慢。根据perf,最后三行的平均运行时间为10.4us,比枚举代码只需1.38us差得多。这仍然比硬编码版本更糟糕,硬编码版本只在0.41美元内运行。这些结果可能会产生误导,尤其是如果它们包含导入:想法是只对查找进行一次预包装,然后派生索引,使其成为一个numpy数组,而不是需要包装的列表。通过避免数据类型计算,您可能会获得一些收益,因此我在中对其进行了编辑。