首先,如果您使用的是 Python 2.x,则可以通过使用xrange()
而不是range()
. 在 Python 3.xxrange()
中没有.range()
xrange()
接下来,如果我们追求速度,我们需要编写更少的代码,并更多地依赖 Python 的内置功能(为了速度而用 C 语言编写的)。
您可以通过使用生成器表达式来加快速度,sum()
如下所示:
from itertools import izip
def find_best(weights,fields):
winner = -1
best = -float('inf')
for c in xrange(num_category):
score = sum(float(t[0]) * t[1] for t in izip(fields, weights[c]))
if score > best:
best = score
winner = c
return winner
再次应用相同的想法,让我们尝试使用max()
以找到最佳结果。我认为这段代码看起来很难看,但是如果您对其进行基准测试并且速度足够快,那么它可能是值得的:
from itertools import izip
def find_best(weights, fields):
tup = max(
((i, sum(float(t[0]) * t[1] for t in izip(fields, wlist))) for i, wlist in enumerate(weights)),
key=lambda t: t[1]
)
return tup[0]
啊! 但如果我没有犯任何错误,这也是同样的事情,它应该在很大程度上依赖于 Python 中的 C 机制。测量它,看看它是否更快。
所以,我们正在调用max()
. 我们给它一个生成器表达式,它会找到生成器表达式返回的最大值。但是你想要最佳值的索引,所以生成器表达式返回一个元组:索引和权重值。所以我们需要将生成器表达式作为第一个参数传递,第二个参数必须是一个从元组中查看权重值并忽略索引的键函数。由于生成器表达式不是它的唯一参数,max()
因此它需要放在括号中。然后它建立一个元组i
和计算的权重,由sum()
我们上面使用的相同计算。最后,一旦我们从我们的索引中取回一个元组max()
以获取索引值,并返回它。
如果我们分解一个函数,我们可以让它变得不那么难看。这增加了函数调用的开销,但如果你测量它,我敢打赌它不会太慢。fields
另外,现在我考虑一下,建立一个已经预先强制到的值列表是有意义的float
;那么我们可以多次使用它。此外,与其使用izip()
并行迭代两个列表,不如创建一个迭代器并明确地向它询问值。在 Python 2.x 中,我们使用.next()
方法函数来请求一个值;在 Python 3.x 中,您将使用next()
内置函数。
def fweight(field_float_list, wlist):
f = iter(field_float_list)
return sum(f.next() * w for w in wlist)
def find_best(weights, fields):
flst = [float(x) for x in fields]
tup = max(
((i, fweight(flst, wlist)) for i, wlist in enumerate(weights)),
key=lambda t: t[1]
)
return tup[0]
如果有 30K 字段值,那么预先计算这些float()
值可能会在速度上取得巨大的胜利。
编辑:我错过了一个技巧。而不是lambda
函数,我应该operator.itemgetter()
像接受答案中的一些代码一样使用。此外,接受的答案是定时的,看起来函数调用的开销很大。但是 Numpy 的答案要快得多,以至于不再值得玩这个答案了。
至于第二部分,我认为它不能加快速度。我会尽力:
def update_weights(weights,fields,toincrease,todecrease):
w_inc = weights[toincrease]
w_dec = weights[todecrease]
for i, f in enumerated(fields):
f = float(f) # see note below
w_inc[i] += f
w_dec[i] -= f
xrange()
所以,这里我们直接迭代字段值,而不是迭代一个。我们有一条强制浮动的线。
请注意,如果权重值已经是浮动的,我们实际上不需要强制在此处浮动,我们可以通过删除该行来节省时间。
您的代码对权重列表进行了四次索引:两次进行增量,两次进行减量。此代码只执行第一个索引(使用toincrease
or todecrease
)参数一次。它仍然必须索引i
才能+=
工作。(我的第一个版本试图用迭代器来避免这种情况,但没有奏效。我应该在发布之前进行测试。但现在已经修复了。)
最后一个尝试的版本:不要在我们进行时递增和递减值,只需使用列表推导来构建一个包含我们想要的值的新列表:
def update_weights(weights, field_float_list, toincrease, todecrease):
f = iter(field_float_list)
weights[toincrease] = [x + f.next() for x in weights[toincrease]]
f = iter(field_float_list)
weights[todecrease] = [x - f.next() for x in weights[todecrease]]
这假设您已经强制所有字段值浮动,如上所示。
以这种方式替换整个列表是更快还是更慢?我会猜得更快,但我不确定。测量并查看!
哦,我应该补充一下:请注意,我update_weights()
上面显示的版本不返回weights
. 这是因为在 Python 中,不从改变数据结构的函数返回值被认为是一种很好的做法,只是为了确保没有人对哪些函数进行查询以及哪些函数改变事物感到困惑。
http://en.wikipedia.org/wiki/Command-query_separation
测量测量测量。看看我的建议有多快,或者不是。