Efficient way to rotate a list in python

后端 未结 26 1181
一生所求
一生所求 2020-11-22 03:14

What is the most efficient way to rotate a list in python? Right now I have something like this:

>>> def rotate(l, n):
...     return l[n:] + l[:n]         


        
相关标签:
26条回答
  • 2020-11-22 03:51

    Just some notes on timing:

    If you're starting with a list, l.append(l.pop(0)) is the fastest method you can use. This can be shown with time complexity alone:

    • deque.rotate is O(k) (k=number of elements)
    • list to deque conversion is O(n)
    • list.append and list.pop are both O(1)

    So if you are starting with deque objects, you can deque.rotate() at the cost of O(k). But, if the starting point is a list, the time complexity of using deque.rotate() is O(n). l.append(l.pop(0) is faster at O(1).

    Just for the sake of illustration, here are some sample timings on 1M iterations:

    Methods which require type conversion:

    • deque.rotate with deque object: 0.12380790710449219 seconds (fastest)
    • deque.rotate with type conversion: 6.853878974914551 seconds
    • np.roll with nparray: 6.0491721630096436 seconds
    • np.roll with type conversion: 27.558452129364014 seconds

    List methods mentioned here:

    • l.append(l.pop(0)): 0.32483696937561035 seconds (fastest)
    • "shiftInPlace": 4.819645881652832 seconds
    • ...

    Timing code used is below.


    collections.deque

    Showing that creating deques from lists is 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
    

    If you need to create deque objects:

    1M iterations @ 6.853878974914551 seconds

    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)
    

    If you already have deque objects:

    1M iterations @ 0.12380790710449219 seconds

    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

    If you need to create nparrays

    1M iterations @ 27.558452129364014 seconds

    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
    """
    

    If you already have nparrays:

    1M iterations @ 6.0491721630096436 seconds

    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)
    

    "Shift in place"

    Requires no type conversion

    1M iterations @ 4.819645881652832 seconds

    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))

    Requires no type conversion

    1M iterations @ 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)
    
    0 讨论(0)
  • 2020-11-22 03:51

    I take this cost model as a reference:

    http://scripts.mit.edu/~6.006/fall07/wiki/index.php?title=Python_Cost_Model

    Your method of slicing the list and concatenating two sub-lists are linear-time operations. I would suggest using pop, which is a constant-time operation, e.g.:

    def shift(list, n):
        for i in range(n)
            temp = list.pop()
            list.insert(0, temp)
    
    0 讨论(0)
  • 2020-11-22 03:55

    If you just want to iterate over these sets of elements rather than construct a separate data structure, consider using iterators to construct a generator expression:

    def shift(l,n):
        return itertools.islice(itertools.cycle(l),n,n+len(l))
    
    >>> list(shift([1,2,3],1))
    [2, 3, 1]
    
    0 讨论(0)
  • 2020-11-22 03:57

    Simplest way I can think of:

    a.append(a.pop(0))
    
    0 讨论(0)
  • 2020-11-22 03:57

    Another alternative:

    def move(arr, n):
        return [arr[(idx-n) % len(arr)] for idx,_ in enumerate(arr)]
    
    0 讨论(0)
  • 2020-11-22 03:58

    A collections.deque is optimized for pulling and pushing on both ends. They even have a dedicated rotate() method.

    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]
    
    0 讨论(0)
提交回复
热议问题