在python中旋转列表最有效的方法是什么? 现在我有这样的东西:
>>> def rotate(l, n):
... return l[n:] + l[:n]
...
>>> l = [1,2,3,4]
>>> rotate(l,1)
[2, 3, 4, 1]
>>> rotate(l,2)
[3, 4, 1, 2]
>>> rotate(l,0)
[1, 2, 3, 4]
>>> rotate(l,-1)
[4, 1, 2, 3]
有没有更好的办法?
在python中旋转列表最有效的方法是什么? 现在我有这样的东西:
>>> def rotate(l, n):
... return l[n:] + l[:n]
...
>>> l = [1,2,3,4]
>>> rotate(l,1)
[2, 3, 4, 1]
>>> rotate(l,2)
[3, 4, 1, 2]
>>> rotate(l,0)
[1, 2, 3, 4]
>>> rotate(l,-1)
[4, 1, 2, 3]
有没有更好的办法?
当前回答
关于时间安排的一些注意事项:
如果从列表开始,l.append(l.pop(0))是可以使用的最快方法。这可以单独用时间复杂度来证明:
双端队列。旋转是O(k) (k=元素数量) 列表到deque的转换是O(n) 列表。附加和列表。pop都是O(1)
所以如果你从deque对象开始,你可以deque.rotate(),代价是O(k)。但是,如果起始点是一个列表,那么使用deque.rotate()的时间复杂度是O(n)。l.append(l.pop(0)在O(1)处更快。
为了便于说明,这里有一些1M迭代的计时示例:
需要类型转换的方法:
双端队列。使用deque对象旋转:0.12380790710449219秒(最快) 双端队列。旋转类型转换:6.853878974914551秒 np。滚动nparray: 6.0491721630096436秒 np。滚带类型转换:27.558452129364014秒
列出这里提到的方法:
L.append (l.pop(0)): 0.32483696937561035秒(最快) "shiftInPlace": 4.819645881652832秒 ...
所用的计时代码如下。
collections.deque
显示从列表创建deques是O(n):
from collections import deque
import big_o
def create_deque_from_list(l):
return deque(l)
best, others = big_o.big_o(create_deque_from_list, lambda n: big_o.datagen.integers(n, -100, 100))
print best
# --> Linear: time = -2.6E-05 + 1.8E-08*n
如果你需要创建deque对象:
1M次迭代@ 6.853878974914551秒
setup_deque_rotate_with_create_deque = """
from collections import deque
import random
l = [random.random() for i in range(1000)]
"""
test_deque_rotate_with_create_deque = """
dl = deque(l)
dl.rotate(-1)
"""
timeit.timeit(test_deque_rotate_with_create_deque, setup_deque_rotate_with_create_deque)
如果你已经有deque对象:
1M次迭代@ 0.12380790710449219秒
setup_deque_rotate_alone = """
from collections import deque
import random
l = [random.random() for i in range(1000)]
dl = deque(l)
"""
test_deque_rotate_alone= """
dl.rotate(-1)
"""
timeit.timeit(test_deque_rotate_alone, setup_deque_rotate_alone)
np.roll
如果你需要创建nparray
1百万次迭代@ 27.558452129364014秒
setup_np_roll_with_create_npa = """
import numpy as np
import random
l = [random.random() for i in range(1000)]
"""
test_np_roll_with_create_npa = """
np.roll(l,-1) # implicit conversion of l to np.nparray
"""
如果你已经有nparray:
1M次迭代@ 6.0491721630096436秒
setup_np_roll_alone = """
import numpy as np
import random
l = [random.random() for i in range(1000)]
npa = np.array(l)
"""
test_roll_alone = """
np.roll(npa,-1)
"""
timeit.timeit(test_roll_alone, setup_np_roll_alone)
“转移到位”
不需要类型转换
1M次迭代@ 4.819645881652832秒
setup_shift_in_place="""
import random
l = [random.random() for i in range(1000)]
def shiftInPlace(l, n):
n = n % len(l)
head = l[:n]
l[:n] = []
l.extend(head)
return l
"""
test_shift_in_place="""
shiftInPlace(l,-1)
"""
timeit.timeit(test_shift_in_place, setup_shift_in_place)
l.append(l.pop(0))
不需要类型转换
1M迭代@ 0.32483696937561035
setup_append_pop="""
import random
l = [random.random() for i in range(1000)]
"""
test_append_pop="""
l.append(l.pop(0))
"""
timeit.timeit(test_append_pop, setup_append_pop)
其他回答
我能想到的最简单的方法:
a.append(a.pop(0))
如果效率是你的目标,(周期?内存?),您最好查看数组模块:http://docs.python.org/library/array.html
数组没有列表的开销。
就纯粹的列表而言,你所拥有的就是你所希望做的。
另一个选择:
def move(arr, n):
return [arr[(idx-n) % len(arr)] for idx,_ in enumerate(arr)]
可能更适合使用ringbuffer。它不是一个列表,尽管出于您的目的,它的行为可能足够像一个列表。
问题是列表上移位的效率是O(n),这对于足够大的列表来说非常重要。
在环缓冲区中移动只是更新了头的位置也就是O(1)
我以这个成本模型作为参考:
http://scripts.mit.edu/~6.006/fall07/wiki/index.php?title=Python_Cost_Model
切片列表和连接两个子列表的方法是线性时间操作。我建议使用pop,这是一个常数时间操作,例如:
def shift(list, n):
for i in range(n)
temp = list.pop()
list.insert(0, temp)