我做了功课,我无意中发现在算法的速度一个奇怪的矛盾。 这里是2个版本的与1个差别相同功能BUR的代码:在第一版本我使用3次排列生成过滤掉一些阵列和在第二版本我使用1 for循环用3 if语句做相同的过滤器的工作。
所以,这里是第一个版本的代码:
def kth_order_statistic(array, k):
pivot = (array[0] + array[len(array) - 1]) // 2
l = [x for x in array if x < pivot]
m = [x for x in array if x == pivot]
r = [x for x in array if x > pivot]
if k <= len(l):
return kth_order_statistic(l, k)
elif k > len(l) + len(m):
return kth_order_statistic(r, k - len(l) - len(m))
else:
return m[0]
而这里的第二个版本的代码:
def kth_order_statistic2(array, k):
pivot = (array[0] + array[len(array) - 1]) // 2
l = []
m = []
r = []
for x in array:
if x < pivot:
l.append(x)
elif x > pivot:
r.append(x)
else:
m.append(x)
if k <= len(l):
return kth_order_statistic2(l, k)
elif k > len(l) + len(m):
return kth_order_statistic2(r, k - len(l) - len(m))
else:
return m[0]
IPython的输出1版本:
In [4]: %%timeit
...: A = range(100000)
...: shuffle(A)
...: k = randint(1, len(A)-1)
...: order_statisctic(A, k)
...:
10 loops, best of 3: 120 ms per loop
而对于第二个版本:
In [5]: %%timeit
...: A = range(100000)
...: shuffle(A)
...: k = randint(1, len(A)-1)
...: kth_order_statistic2(A, k)
...:
10 loops, best of 3: 169 ms per loop
那么,为什么第一个版本比第二快? 我还使用滤波器()函数而不是阵列发生器由第三版本至极和它比第二版本更慢(它得到了每循环218毫秒)
Answer 1:
让我们来定义我们需要回答的问题和timeit它们的功能:
In [18]: def iter():
l = [x for x in range(100) if x > 10]
....:
In [19]: %timeit iter()
100000 loops, best of 3: 7.92 µs per loop
In [20]: def loop():
l = []
for x in range(100):
if x > 10:
l.append(x)
....:
In [21]: %timeit loop()
10000 loops, best of 3: 20 µs per loop
In [22]: def loop_fast():
l = []
for x in range(100):
if x > 10:
pass
....:
In [23]: %timeit loop_fast()
100000 loops, best of 3: 4.69 µs per loop
我们可以看到,在没有追加命令循环是一样快的列表理解。 其实,如果我们看看字节码,我们可以看到,在列表理解蟒蛇的情况下能够使用内置的字节码指令称为LIST_APPEND代替:
- 加载列表:40 LOAD_FAST
- 加载属性:43 LOAD_ATTRIBUTE
- 调用加载的功能:49 CALL_FUNCTION
- 卸载列表(?):52 POP_TOP
你可以从以前的字节码下面的输出中看到缺少与列表中理解和使用“loop_fast”功能。 这三个功能的使用timeit比较明显,那些有责任的三种方法的不同时机。
In [27]: dis.dis(iter)
2 0 BUILD_LIST 0
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (1)
9 LOAD_CONST 2 (100)
12 CALL_FUNCTION 2
15 GET_ITER
>> 16 FOR_ITER 24 (to 43)
19 STORE_FAST 0 (x)
22 LOAD_FAST 0 (x)
25 LOAD_CONST 2 (100)
28 COMPARE_OP 4 (>)
31 POP_JUMP_IF_FALSE 16
34 LOAD_FAST 0 (x)
37 LIST_APPEND 2
40 JUMP_ABSOLUTE 16
>> 43 STORE_FAST 1 (l)
46 LOAD_CONST 0 (None)
49 RETURN_VALUE
In [28]: dis.dis(loop)
2 0 BUILD_LIST 0
3 STORE_FAST 0 (1)
3 6 SETUP_LOOP 51 (to 60)
9 LOAD_GLOBAL 0 (range)
12 LOAD_CONST 1 (1)
15 LOAD_CONST 2 (100)
18 CALL_FUNCTION 2
21 GET_ITER
>> 22 FOR_ITER 34 (to 59)
25 STORE_FAST 1 (x)
4 28 LOAD_FAST 1 (x)
31 LOAD_CONST 3 (10)
34 COMPARE_OP 4 (>)
37 POP_JUMP_IF_FALSE 22
5 40 LOAD_FAST 0 (l)
43 LOAD_ATTR 1 (append)
46 LOAD_FAST 1 (x)
49 CALL_FUNCTION 1
52 POP_TOP
53 JUMP_ABSOLUTE 22
56 JUMP_ABSOLUTE 22
>> 59 POP_BLOCK
>> 60 LOAD_CONST 0 (None)
63 RETURN_VALUE
In [29]: dis.dis(loop_fast)
2 0 BUILD_LIST 0
3 STORE_FAST 0 (1)
3 6 SETUP_LOOP 38 (to 47)
9 LOAD_GLOBAL 0 (range)
12 LOAD_CONST 1 (1)
15 LOAD_CONST 2 (100)
18 CALL_FUNCTION 2
21 GET_ITER
>> 22 FOR_ITER 21 (to 46)
25 STORE_FAST 1 (x)
4 28 LOAD_FAST 1 (x)
31 LOAD_CONST 3 (10)
34 COMPARE_OP 4 (>)
37 POP_JUMP_IF_FALSE 22
5 40 JUMP_ABSOLUTE 22
43 JUMP_ABSOLUTE 22
>> 46 POP_BLOCK
>> 47 LOAD_CONST 0 (None)
50 RETURN_VALUE
Answer 2:
使用简单for
快于list comprehesion
。 这是快了近2倍。 检查下面的结果:
使用list comprehension
:58微秒
moin@moin-pc:~$ python -m timeit "[i for i in range(1000)]"
10000 loops, best of 3: 58 usec per loop
使用for
循环:37.1微秒
moin@moin-pc:~$ python -m timeit "for i in range(1000): i"
10000 loops, best of 3: 37.1 usec per loop
但是,在你的情况, for
正在花费更多的时间比列表理解,不是因为你的循环很慢。 但由于.append()
您所使用的代码中。
与append()
在for
loop`:114微秒
moin@moin-pc:~$ python -m timeit "my_list = []" "for i in range(1000): my_list.append(i)"
10000 loops, best of 3: 114 usec per loop
这清楚地表明,它.append()
它正在通过两次拍摄的时间for
循环 。
然而, storing the "list.append" in different variable
:69.3微秒
moin@moin-pc:~$ python -m timeit "my_list = []; append = my_list.append" "for i in range(1000): append(i)"
10000 loops, best of 3: 69.3 usec per loop
相比于上述比较过去的情况,并且结果是相当媲美的还有在性能巨大的改进list comprehension
。 这意味着代替调用, my_list.append()
各一次,性能可通过存储的函数在另一个变量即参考加以改进append_func = my_list.append
使用和制备的呼叫变量append_func(i)
这也证明了, 它是更快相比,直接使得使用类的对象调用函数调用存储在变量类的功能 。
感谢您斯特凡在通知使最后一种情况。
Answer 3:
让我们消散疑问: 第二个版本是稍快: 列表理解的速度更快 ,但两个数组循环和尽可能多的条件语句在一次迭代中被丢弃。
def kth_order_statistic1(array,k):
pivot = (array[0] + array[len(array) - 1]) // 2
l = [x for x in array if x < pivot]
m = [x for x in array if x == pivot]
r = [x for x in array if x > pivot]
if k <= len(l):
return kth_order_statistic1(l, k)
elif k > len(l) + len(m):
return kth_order_statistic1(r, k - len(l) - len(m))
else:
return m[0]
def kth_order_statistic2(array,k):
pivot = (array[0] + array[len(array) - 1]) // 2
l = []
m = []
r = []
for x in array:
if x < pivot:
l.append(x)
elif x > pivot:
r.append(x)
else:
m.append(x)
if k <= len(l):
return kth_order_statistic2(l, k)
elif k > len(l) + len(m):
return kth_order_statistic2(r, k - len(l) - len(m))
else:
return m[0]
def kth_order_statistic3(array,k):
pivot = (array[0] + array[len(array) - 1]) // 2
l = []
m = []
r = []
for x in array:
if x < pivot: l.append(x)
for x in array:
if x== pivot: m.append(x)
for x in array:
if x > pivot: r.append(x)
if k <= len(l):
return kth_order_statistic3(l, k)
elif k > len(l) + len(m):
return kth_order_statistic3(r, k - len(l) - len(m))
else:
return m[0]
import time
import random
if __name__ == '__main__':
A = range(100000)
random.shuffle(A)
k = random.randint(1, len(A)-1)
start_time = time.time()
for x in range(1000) :
kth_order_statistic1(A,k)
print("--- %s seconds ---" % (time.time() - start_time))
start_time = time.time()
for x in range(1000) :
kth_order_statistic2(A,k)
print("--- %s seconds ---" % (time.time() - start_time))
start_time = time.time()
for x in range(1000) :
kth_order_statistic3(A,k)
print("--- %s seconds ---" % (time.time() - start_time))
python :
--- 25.8894710541 seconds ---
--- 24.073086977 seconds ---
--- 32.9823839664 seconds ---
ipython
--- 25.7450709343 seconds ---
--- 22.7140650749 seconds ---
--- 35.2958850861 seconds ---
的定时可以根据随机拉伸而变化,但三者之间的差别是非常相同的。
Answer 4:
算法的结构不同和条件结构将被牵连。 测试以追加到r和m可以通过前面的测试被丢弃。 关于for循环具有更严格比较append
,以及列表理解是对非最佳以下
for x in array:
if x < pivot:
l.append(x)
for x in array:
if x== pivot:
m.append(x)
for x in array:
if x > pivot:
r.append(x)
文章来源: Comparing list comprehensions and explicit loops (3 array generators faster than 1 for loop)