首先,如果你正在使用Python 2.x,你可以使用
xrange()
代替
range()
来提高一些速度。在Python 3.x中没有
xrange()
,但是内置的
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()
值可能会大大提高速度。
编辑:我错过了一个技巧。 我应该使用
operator.itemgetter()
而不是
lambda
函数,就像接受的答案中的一些代码一样。 此外,接受的答案对时间进行了计时,看起来函数调用的开销很大。 但是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)
w_inc[i] += f
w_dec[i] -= f
因此,我们不是通过迭代
xrange()
,而是直接迭代字段值。我们有一行代码将其强制转换为浮点数。
请注意,如果权重值已经是浮点数,则我们不需要在此处强制转换为浮点数,只需删除该行即可节省时间。
您的代码四次索引权重列表:两次执行增量,两次执行减量。此代码仅使用
toincrease
或
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
测量、测量、再测量。看看我的建议有多快,或者有多慢。
if score > best:
开头的代码块是否应该减少一个缩进级别? - huonfloat
? - huon