我有这个功能
def f(l):
stack = []
for i in range(len(l)):
if i == 0:
n0 = some_function(l[0], l[0])
stack.append(n0)
else:
n = some_function(l[i], stack[-1])
stack.append(n)
return stack
其中some_function需要2个浮点数进行一些简单的数学运算并返回一个浮点数。 问题是f的参数长度可能很大,我必须在结果上循环n次,即:
l = [float_0, float_1, ..., float_m]
for i in range(n):
l = f(l)
问题是,如何使这个简单的算法更快?我知道地图有点提高“速度”,但我不知道在这种情况下如何使用它。
任何其他想法都很有用。
非常感谢!!!
(如果我有一些语言错误,请放纵,英语不是我的母语)
答案 0 :(得分:0)
也许使用列表理解:
def f(l):
last = l[-1]
return [some_function(l[0], l[0])] + [some_function(x, last) for x in l[1:]]
答案 1 :(得分:0)
用地图或列表理解来代替循环迭代,最多只能提高速度 - 我的猜测最多只能是2倍。但是使用理解或地图的最后计算值是棘手的(但并非不可能)。
你的职能:
def f0(ll, fun):
stack = []
for i in range(len(ll)):
if i == 0:
n0 = fun(ll[0],ll[0])
stack.append(n0)
else:
n = fun(ll[i], stack[-1])
stack.append(n)
return stack
示例功能:
def foo(a,b):
return a + b
简化你的功能:
def f1(ll, fun, init):
stack = []
for l in ll:
stack.append(fun(l, init))
init = stack[-1]
return stack
def f11(ll, fun, init): # a bit better
stack = []
for l in ll:
init = fun(l, init)
stack.append(init)
return stack
测试:
In [784]: ll=list(range(10,20))
In [785]: f0(ll,foo)
Out[785]: [20, 31, 43, 56, 70, 85, 101, 118, 136, 155]
In [786]: f1(ll,foo,ll[0])
Out[786]: [20, 31, 43, 56, 70, 85, 101, 118, 136, 155]
In [818]: f11(ll,foo,ll[0])
Out[818]: [20, 31, 43, 56, 70, 85, 101, 118, 136, 155]
时间测试:
In [787]: llb=list(range(10,2000))
In [788]: timeit f0(llb,foo)
1000 loops, best of 3: 1 ms per loop
In [789]: timeit f1(llb,foo,llb[0])
1000 loops, best of 3: 744 µs per loop
In [819]: timeit f11(llb,foo,llb[0])
1000 loops, best of 3: 630 µs per loop
发电机方法:
def g0(ll, fun, init):
for l in ll:
init = fun(l, init)
yield init
In [804]: list(g0(ll, foo, ll[0]))
Out[804]: [20, 31, 43, 56, 70, 85, 101, 118, 136, 155]
In [805]: timeit list(g0(llb, foo, llb[0]))
1000 loops, best of 3: 509 µs per loop
使用预先分配的结果(在评论中也由@Alfe
建议):
def f2(ll, fun, init):
res=ll[:]
res[0]=init
for i,v in enumerate(ll):
init=fun(v,init)
res[i]=init
return res
In [808]: f2(ll,foo,ll[0])
Out[808]: [20, 31, 43, 56, 70, 85, 101, 118, 136, 155]
In [809]: timeit f2(llb,foo,llb[0])
1000 loops, best of 3: 583 µs per loop
通过使用reduce
In [790]: from functools import reduce # needed for py3
In [791]: reduce(foo,ll,ll[0])
Out[791]: 155
In [792]: timeit reduce(foo,llb,llb[0])
1000 loops, best of 3: 394 µs per loop
reduce
执行此操作,但仅返回最后一个值。
Cumulative sum with list comprehension告诉我们Py3 itertools
有一个accumulate
函数:
In [797]: list(itertools.accumulate(ll,foo))
Out[797]: [10, 21, 33, 46, 60, 75, 91, 108, 126, 145]
In [798]: timeit list(itertools.accumulate(llb,foo))
1000 loops, best of 3: 489 µs per loop
不幸的是,accumulate
没有采用初始值参数(reduce
会这样做。)
让我们了解列表理解和地图可以为我们做些什么,而不是试图捕捉累积行为:
In [799]: timeit [foo(a,0) for a in llb]
1000 loops, best of 3: 471 µs per loop
In [800]: timeit list(map(lambda a: foo(a,0), llb))
1000 loops, best of 3: 686 µs per loop
发电机方法看起来很不错。这是正确的,它的时间接近accumulate
和列表理解的时间。简化功能评估会产生最大的差异。在列表中收集值的方法并不那么重要。
如果您有numpy
,则可以将问题转换为ufunc.accumulate
。例如,这个简单的求和:
In [811]: np.cumsum(ll)
Out[811]: array([ 10, 21, 33, 46, 60, 75, 91, 108, 126, 145], dtype=int32)
In [812]: np.cumsum?
In [813]: timeit np.cumsum(llb)
1000 loops, best of 3: 211 µs per loop
https://docs.python.org/3/library/itertools.html#itertool-functions
可以通过在iterable中提供初始值并仅使用func参数中的累计总数来建模一阶递归关系
所以:
In [827]: list(itertools.accumulate([ll[0]]+ll,foo))[1:]
Out[827]: [20, 31, 43, 56, 70, 85, 101, 118, 136, 155]
In [828]: timeit list(itertools.accumulate([llb[0]]+llb,foo))[1:]
1000 loops, best of 3: 478 µs per loop
虽然这取决于foo(a,0)
返回a
。