提高Python中`compute_optimal_weights`函数的性能

时间:2017-04-20 03:18:26

标签: python performance dictionary list-comprehension

是否有更快的方法在Python中编写“compute_optimal_weights”函数。我运行了数亿次,所以任何速度增加都会有所帮助。每次运行时,函数的参数都不同。

c1 = 0.25
c2 = 0.67

def compute_optimal_weights(input_prices):
    input_weights_optimal = {}
    for i in input_prices:
        price = input_prices[i]
        input_weights_optimal[i] = c2 / sum([(price/n) ** c1 for n in input_prices.values()])
    return input_weights_optimal

input_sellers_ID = range(10)
input_prices = {}
for i in input_sellers_ID:
    input_prices[i] = random.uniform(0,1)


t0 = time.time()
for i in xrange(1000000):
    compute_optimal_weights(input_prices)
t1 = time.time()
print "old time", (t1 - t0)

列表和字典中的元素数量各不相同,但平均而言大约有10个元素。 input_prices中的键在所有调用中都是相同的,但值会更改,因此相同的键在不同的运行中将具有不同的值。

2 个答案:

答案 0 :(得分:1)

我相信我们可以通过分解循环来加速这个功能。如果我的数学没有错误,请a = priceb = nc = c1(例如(5/6)**3 == 5**3 / 6**3

(5./6.)**2 + (5./4.)**2
== 
5**2 / 6.**2 + 5**2 / 4.**2
== 
5**2 * (1/6.**2 + 1/4.**2)

使用变量:

sum( (a / b) ** c for each b)
==
sum( a**c * (1/b) ** c for each b)
==
a**c * sum((1./b)**c for each b)

第二个词是不变的,可以取出。离开了:

更快的实施 - 原始Python

使用生成器和字典理解:

def compute_optimal_weights(input_prices):
    sconst = sum(1/w**c1 for w in input_prices.values())
    return {k: c2 / (v**c1 * sconst) for k, v in input_prices.items()}

注意:如果您使用Python2,请将.values().items()替换为.itervalues().iteritems()以获得额外的加速(使用大型列表时只需几毫秒)。

更快 - Numpy

此外,如果您不太关心字典并只想要这些值,您可以使用numpy加速它(对于大输入>100):

def compute_optimal_weights_np(input_prices):
    data = np.asarray(input_prices.values()) ** c1
    return c2 / (data * np.sum(1./data))

不同输入大小的时间很少:

  • N = 10输入:

    MINE:  100000 loops, best of 3: 6.02 µs per loop
    NUMPY: 100000 loops, best of 3: 10.6 µs per loop
    YOURS: 10000 loops, best of 3: 23.8 µs per loop
    
  • N = 100输入:

    MINE:  10000 loops, best of 3: 49.1 µs per loop
    NUMPY: 10000 loops, best of 3: 22.6 µs per loop
    YOURS: 1000 loops, best of 3: 1.86 ms per loop
    
  • N = 1000输入:

    MINE:  1000 loops, best of 3: 458 µs per loop
    NUMPY: 10000 loops, best of 3: 121 µs per loop
    YOURS: 10 loops, best of 3: 173 ms per loop
    
  • N = 100000输入:

    MINE:  10 loops, best of 3: 54.2 ms per loop
    NUMPY: 100 loops, best of 3: 11.1 ms per loop
    YOURS: didn't finish in a couple of minutes
    

这里的两个选项都比问题中提出的选项快得多。如果你可以提供一致的输入(以数组而不是字典的形式),使用numpy的好处在大小增加时变得明显:

答案 1 :(得分:1)

使用一点数学运算,您可以在循环早期计算部分sum_price_ratio_scaled作为常数,并将程序加速 ~80%(对于平均输入大小) 10)。

<小时/>

优化实施(Python 3):

def compute_optimal_weights(ids, prices):
    scaled_sum = 0
    for i in ids:
        scaled_sum += prices[i] ** -0.25
    result = {}
    for i in ids:
        result[i] = 0.67 * (prices[i] ** -0.25) / scaled_sum
    return result

编辑,以响应this answer虽然使用numpy将会证明对大量数据集更有效,但鉴于“平均有大约10个元素“在您的input_sellers_ID列表中,我怀疑这种方法对您的特定应用程序来说是值得的。

虽然利用生成器表达式和字典理解的简洁性可能很诱人,但我注意到在我的机器上运行时,通过使用常规for-in循环并避免像{{1}这样的函数调用来获得最佳性能。 }。但是,为了完整起见,以上实现的内容将更加“pythonic”:

sum(...)

<小时/>

推理/数学:

根据您发布的算法,您尝试使用下面的函数def compute_optimal_weights(ids, prices): scaled_sum = sum(prices[i] ** -0.25 for i in ids) return {i: 0.67 * (prices[i] ** -0.25) / scaled_sum for i in ids} 创建一个字典,其中f(i)i列表中的元素之一。< / p>

当您最初写出input_sellers_ID的公式时,似乎必须为求和过程的每一步重新计算f(i),这是昂贵的。但是,使用指数规则简化表达式,您可以看到确定prices[i]所需的最简单求和实际上是f(i)独立(仅i的索引值曾经使用过1}},这意味着该术语是一个常量,可以在设置字典值的循环之外计算。

enter image description here

请注意,上面我将j称为input_prices,将prices称为input_sellers_ID

<小时/>

性能概况(我的机器速度提高约80%,尺寸10):

ids