在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)
其他回答
如果效率是你的目标,(周期?内存?),您最好查看数组模块:http://docs.python.org/library/array.html
数组没有列表的开销。
就纯粹的列表而言,你所拥有的就是你所希望做的。
deque对两端的拉和推进行了优化。它们甚至有一个专用的rotate()方法。
from collections import deque
items = deque([1, 2])
items.append(3) # deque == [1, 2, 3]
items.rotate(1) # The deque is now: [3, 1, 2]
items.rotate(-1) # Returns deque to original state: [1, 2, 3]
item = items.popleft() # deque == [2, 3]
我也对此感兴趣,并将一些建议的解决方案与perfplot(我的一个小项目)进行了比较。
事实证明凯利·邦迪的建议
tmp = data[shift:]
tmp += data[:shift]
在所有轮班中都表现良好。
从本质上讲,perfplot执行增加大型数组的移位并测量时间。以下是调查结果:
Shift = 1:
Shift = 100:
代码重现情节:
import numpy
import perfplot
import collections
shift = 100
def list_append(data):
return data[shift:] + data[:shift]
def list_append2(data):
tmp = data[shift:]
tmp += data[:shift]
return tmp
def shift_concatenate(data):
return numpy.concatenate([data[shift:], data[:shift]])
def roll(data):
return numpy.roll(data, -shift)
def collections_deque(data):
items = collections.deque(data)
items.rotate(-shift)
return items
def pop_append(data):
data = data.copy()
for _ in range(shift):
data.append(data.pop(0))
return data
b = perfplot.bench(
setup=lambda n: numpy.random.rand(n).tolist(),
kernels=[
list_append,
list_append2,
roll,
shift_concatenate,
collections_deque,
pop_append,
],
n_range=[2 ** k for k in range(7, 20)],
xlabel="len(data)",
)
b.show()
b.save("shift100.png")
用例是什么?通常,我们并不需要完全移位的数组——我们只需要访问移位数组中的少量元素。
获取Python切片是运行时O(k),其中k是切片,因此切片旋转是运行时n。deque旋转命令也是O(k)。我们能做得更好吗?
考虑一个非常大的数组(比方说,大到切片的计算速度很慢)。另一种解决方案是保留原始数组,并简单地计算在某种移位后存在于我们所期望的索引中的项的索引。
访问移位的元素就变成了O(1)。
def get_shifted_element(original_list, shift_to_left, index_in_shifted):
# back calculate the original index by reversing the left shift
idx_original = (index_in_shifted + shift_to_left) % len(original_list)
return original_list[idx_original]
my_list = [1, 2, 3, 4, 5]
print get_shifted_element(my_list, 1, 2) ----> outputs 4
print get_shifted_element(my_list, -2, 3) -----> outputs 2
我以这个成本模型作为参考:
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)