在python中移动列表的有效方法

dzh*_*lil 244 python list

在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]
Run Code Online (Sandbox Code Playgroud)

有没有更好的办法?

Ign*_*ams 260

A collections.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]
Run Code Online (Sandbox Code Playgroud)

  • 详细说来,`deque.rotate`是O(k)但是从列表到双端的**类型转换是O(n)**.因此,如果从列表开始,则使用deque.rotate是O(n)+ O(k)= O(n).另一方面,`l.append(l.pop(0))`是O(1). (8认同)
  • 对于未来的读者:`collections.deque rotate()`比切片更快,根据https://wiki.python.org/moin/TimeComplexity (6认同)
  • 但请注意,使用 `deque.rotate` 需要先将类型转换为 `deque` 对象,这比 `l.append(l.pop(0))` 慢。因此,如果您有一个 deque 对象开始,请确保它是最快的。否则,使用`l.append(l.pop(0))`。 (2认同)
  • @Purrell,弹出前面的项目是O(n).在wiki.python.org/moin/TimeComplexity中,它被列为O(k),k是弹出项后面的列表中元素的数量,因为数据结构将所有后续元素移到列表的前面.由于这个原因,只有最后一个元素可以在O(1)时间内弹出. (2认同)

Jam*_*old 86

刚刚使用pop(0)怎么样?

list.pop([i])

删除列表中给定位置的项目,然后将其返回.如果未指定索引,则a.pop()删除并返回列表中的最后一项.(i方法签名周围的方括号表示该参数是可选的,而不是您应该在该位置键入方括号.您将在Python Library Reference中经常看到这种表示法.)

  • 但是不会花费O(k)来移除列表中的k是剩余元素数量的每个元素.所以总时间将是O(n ^ 2)http://wiki.python.org/moin/TimeComplexity (13认同)
  • 这并没有真正回答这个问题.问题不在于按顺序返回项目,而在于创建一个不同顺序的新列表. (5认同)
  • 不,使用pop的问题的答案是`l.append(l.pop(0)`.如果我没记错,那就是O(1). (4认同)
  • list.pop在内部调用list_ass_slice,它使用记忆功能快速移动所有项目,但仍为O(n)。参见https://github.com/python/cpython/blob/master/Objects/listobject.c和https://wiki.python.org/moin/TimeComplexity。可以在恒定时间内从python列表中删除的唯一项是最后一项。 (4认同)
  • 投反对票。来自 https://docs.python.org/3/tutorial/datastructures.html#using-lists-as-queues 也可以将列表用作队列,其中添加的第一个元素是检索到的第一个元素(“先进先出”);但是,列表在此方面效率不高。虽然从列表末尾追加和弹出很快,但从列表开头插入或弹出很慢(因为所有其他元素都必须移动一个)。 (3认同)

Ric*_*ard 47

Numpy可以使用以下roll命令执行此操作:

>>> import numpy
>>> a=numpy.arange(1,10) #Generate some data
>>> numpy.roll(a,1)
array([9, 1, 2, 3, 4, 5, 6, 7, 8])
>>> numpy.roll(a,-1)
array([2, 3, 4, 5, 6, 7, 8, 9, 1])
>>> numpy.roll(a,5)
array([5, 6, 7, 8, 9, 1, 2, 3, 4])
>>> numpy.roll(a,9)
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢的一点是,有时在答案提要中你可以找到一些像这样的新宝藏:) (3认同)
  • @PeterHarrison:由于您没有提供测试详细信息,因此很难知道您的意思。[此答案](/sf/answers/3143123931/) 提供了完整的测试详细信息和时序比较。 (3认同)
  • 当我测试它时,这非常非常慢 (2认同)

jcd*_*yer 33

这取决于你执行此操作时想要发生的事情:

>>> shift([1,2,3], 14)
Run Code Online (Sandbox Code Playgroud)

你可能想改变你的:

def shift(seq, n):
    return seq[n:]+seq[:n]
Run Code Online (Sandbox Code Playgroud)

至:

def shift(seq, n):
    n = n % len(seq)
    return seq[n:] + seq[:n]
Run Code Online (Sandbox Code Playgroud)

  • 注意:空列表会崩溃. (5认同)

小智 14

我能想到的最简单的方法:

a.append(a.pop(0))
Run Code Online (Sandbox Code Playgroud)

  • 这是最快的列表方式。`collections.deque`速度更快,但是对于单次迭代或多次迭代的列表长度的大多数常见情况,`a.append(a.pop(0))`的速度将比类型转换为双端队列 (2认同)

Phi*_*l H 13

如果您只想迭代这些元素集而不是构造单独的数据结构,请考虑使用迭代器来构造生成器表达式:

def shift(l,n):
    return itertools.islice(itertools.cycle(l),n,n+len(l))

>>> list(shift([1,2,3],1))
[2, 3, 1]
Run Code Online (Sandbox Code Playgroud)


ket*_*urn 11

这还取决于您是否要将列表移位(改变它),或者您希望函数返回新列表.因为,根据我的测试,这样的事情至少比你的实现增加了两个列表快20倍:

def shiftInPlace(l, n):
    n = n % len(l)
    head = l[:n]
    l[:n] = []
    l.extend(head)
    return l
Run Code Online (Sandbox Code Playgroud)

事实上,即使添加一个l = l[:]顶部的操作在传递的列表的副本上仍然是两倍快.

各种实现的各种实现在http://gist.github.com/288272

  • 而不是`l [:n] = []`我会选择`del l [:n]`.只是一个选择. (3认同)
  • @keturn:`del`仍然是Py3中的一个声明.但是`x .__ delitem __(y)<==> del x [y]`,所以如果你更喜欢使用方法,`l .__ delitem __(slice(n))`也是等价的,并且在2和3中都有效. (2认同)

Pur*_*ell 9

关于时间的一些注意事项:

如果您从列表开始,l.append(l.pop(0))则是您可以使用的最快方法.这可以单独显示时间复杂度:

  • deque.rotate是O(k)(k =元素数)
  • 列表到双端转换是O(n)
  • list.append和list.pop都是O(1)

因此,如果您从deque对象开始,则可以deque.rotate()以O(k)为代价.但是,如果起点是列表,则使用的时间复杂度deque.rotate()为O(n).l.append(l.pop(0)在O(1)处更快.

仅为了说明,以下是1M迭代的一些示例时序:

需要类型转换的方法:

  • deque.rotate与deque对象:0.12380790710449219秒(最快)
  • deque.rotate带类型转换:6.853878974914551秒
  • np.roll与nparray:6.0491721630096436秒
  • np.roll带类型转换: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
Run Code Online (Sandbox Code Playgroud)

如果需要创建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)
Run Code Online (Sandbox Code Playgroud)

如果您已经有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)
Run Code Online (Sandbox Code Playgroud)

np.roll

如果你需要创建nparrays

1M次迭代@ 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
"""
Run Code Online (Sandbox Code Playgroud)

如果你已经有nparrays:

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)
Run Code Online (Sandbox Code Playgroud)

"转移到位"

不需要类型转换

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)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

  • list.pop() 是一个常数时间操作,而 list.pop(0) 是_not_。它相对于列表长度以线性时间运行。您可以通过修改 timeit 设置来测试:`l = [random.random() for i in range(100000)]` (2认同)

Nic*_*mer 7

我对此也很感兴趣,并将一些建议的解决方案与perfplot(我的一个小项目)进行了比较。

原来是

for _ in range(n):
    data.append(data.pop(0))
Run Code Online (Sandbox Code Playgroud)

迄今为止小班最快的方法n

对于较大的n

data[n:] + data[:n]
Run Code Online (Sandbox Code Playgroud)

还不错

本质上,perfplot执行移位以增加大型阵列并测量时间。结果如下:

shift = 1

在此处输入图片说明

shift = 100

在此处输入图片说明


复制剧情的代码:

import numpy
import perfplot
import collections


shift = 100


def list_append(data):
    return data[shift:] + data[:shift]


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):
    for _ in range(shift):
        data.append(data.pop(0))
    return data


perfplot.save(
    "shift100.png",
    setup=lambda n: numpy.random.rand(n).tolist(),
    kernels=[list_append, roll, shift_concatenate, collections_deque, pop_append],
    n_range=[2 ** k for k in range(7, 20)],
    logx=True,
    logy=True,
    xlabel="len(data)",
)
Run Code Online (Sandbox Code Playgroud)


Bit*_*der 5

对于不可变的实现,您可以使用以下内容:

def shift(seq, n):
    shifted_seq = []
    for i in range(len(seq)):
        shifted_seq.append(seq[(i-n) % len(seq)])
    return shifted_seq

print shift([1, 2, 3, 4], 1)
Run Code Online (Sandbox Code Playgroud)