在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]

有没有更好的办法?


当前回答

可能更适合使用ringbuffer。它不是一个列表,尽管出于您的目的,它的行为可能足够像一个列表。

问题是列表上移位的效率是O(n),这对于足够大的列表来说非常重要。

在环缓冲区中移动只是更新了头的位置也就是O(1)

其他回答

可能更适合使用ringbuffer。它不是一个列表,尽管出于您的目的,它的行为可能足够像一个列表。

问题是列表上移位的效率是O(n),这对于足够大的列表来说非常重要。

在环缓冲区中移动只是更新了头的位置也就是O(1)

我也对此感兴趣,并将一些建议的解决方案与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")

下面的方法是O(n)到位,辅助内存不变:

def rotate(arr, shift):
  pivot = shift % len(arr)
  dst = 0
  src = pivot
  while (dst != src):
    arr[dst], arr[src] = arr[src], arr[dst]
    dst += 1
    src += 1
    if src == len(arr):
      src = pivot
    elif dst == pivot:
      pivot = src

请注意,在python中,这种方法与其他方法相比效率非常低,因为它不能利用任何部分的本机实现。

我想你想要的是这个:

a.insert(0, x)

另一个选择:

def move(arr, n):
    return [arr[(idx-n) % len(arr)] for idx,_ in enumerate(arr)]