簡體   English   中英

在python中重新定位子列表的最快方法

[英]Fastest way to reposition sublist in python

從Python中的列表重新定位子列表的最快方法是什么?

假設我們有一個列表L = [a,b,c,d,e,f,g,h] ,現在我想取[c,d,e]並將其放在列表中的g之后。 我怎么能快速做到這一點?

編輯:換句話說,我想寫一個函數:

  1. 從L中提取長度為n的子列表L_sub,留下L_temp
  2. 將L_sub的項目在給定位置i插入L_temp

我猜的主要問題是如何盡快將列表插入列表。

我認為OP想要在這里做到這一點。

快速操作的關鍵是最小化列表的創建和列表的縮短/延長。 這意味着我們必須努力始終對列表索引進行1:1分配,因此沒有L[i:i] = L[a:b]且沒有L[a:b] = [] 使用帶insertpop循環更糟糕,因為那樣你會多次縮短和延長列表。 連接列表也很糟糕,因為您首先必須為每個部分創建一個列表,然后為每個+創建一個更大和更大的連接列表。 由於您希望“就地”執行此操作,因此您必須最終將生成的列表分配給L[:]

    # items:   0 | 1   2   3 | 4   5   6   7 | 8   9
    #            a   span1   b     span2     c
    # pos:       1           4               8

    # Result:
    #          0 | 4   5   6   7 | 1   2   3 | 8   9
    #            a     span2         span2   c

讓我們先做一個觀察。 如果a = startb = end = start + length ,而c是插入位置,那么我們希望的操作是切入| 標記和交換span1span2 但是如果b = startc = enda是插入位置,那么我們想要交換span1span2 所以在我們的函數中,我們只處理必須交換的兩個連續段。

我們無法完全避免制作新列表,因為我們需要在移動內容時存儲重疊值。 但是,我們可以通過選擇要存儲到臨時列表中的兩個跨區中的哪一個使列表盡可能短。

def inplace_shift(L, start, length, pos):
    if pos > start + length:
        (a, b, c) = (start, start + length, pos)
    elif pos < start:
        (a, b, c) = (pos, start, start + length)
    else:
        raise ValueError("Cannot shift a subsequence to inside itself")
    if not (0 <= a < b < c <= len(L)):
        msg = "Index check 0 <= {0} < {1} < {2} <= {3} failed."
        raise ValueError(msg.format(a, b, c, len(L)))

    span1, span2 = (b - a, c - b)
    if span1 < span2:
        tmp = L[a:b]
        L[a:a + span2] = L[b:c]
        L[c - span1:c] = tmp
    else:
        tmp = L[b:c]
        L[a + span2:c] = L[a:b]
        L[a:a + span2] = tmp

科斯似乎在他的時間上犯了一個錯誤,所以我在糾正了參數(從startlength計算end )之后用他的代碼重新編寫它們,這些是從最慢到最快的結果。

Nick Craig-Wood: 100 loops, best of 3: 8.58 msec per loop 
vivek: 100 loops, best of 3: 4.36 msec per loop
PaulP.R.O. (deleted?): 1000 loops, best of 3: 838 usec per loop
unbeli: 1000 loops, best of 3: 264 usec per loop
lazyr: 10000 loops, best of 3: 44.6 usec per loop

我沒有測試任何其他方法產生正確的結果。

我會用python子串做到這一點

def subshift(L, start, end, insert_at):
    temp = L[start:end]
    L = L[:start] + L[end:]
    return L[:insert_at] + temp + L[insert_at:]

print subshift(['a','b','c','d','e','f','g','h'], 2, 5, 4)

startend指向要剪切的子字符串的位置(end在通常的python樣式中是非獨占的insert_at指的是在剪切后將子字符串重新插入的位置。

我認為如果子字符串超過一個字符或兩個長度,這將比任何迭代的解決方案更快,因為優秀的C代碼正在進行繁重的工作。

讓我們看看到目前為止我們得到了什么:

def subshift(L, start, end, insert_at):
    'Nick Craig-Wood'
    temp = L[start:end]
    L = L[:start] + L[end:]
    return L[:insert_at] + temp + L[insert_at:]

# (promising but buggy, needs correction;
# see comments at unbeli's answer)
def unbeli(x, start, end, at): 
    'unbeli'
    x[at:at] = x[start:end]
    x[start:end] = []

def subshift2(L, start, length, pos):
    'PaulP.R.O.'
    temp = pos - length
    S = L[start:length+start]
    for i in range(start, temp):
        L[i] = L[i + length]
    for i in range(0,length):
        L[i + temp] = S[i]
    return L

def shift(L,start,n,i):
    'vivek'
    return L[:start]+L[start+n:i]+L[start:start+n]+L[i:]

基准:

> args = range(100000), 3000, 2000, 60000

> timeit subshift(*args)
100 loops, best of 3: 6.43 ms per loop

  > timeit unbeli(*args)
1000000 loops, best of 3: 631 ns per loop

> timeit subshift2(*args)
100 loops, best of 3: 11 ms per loop

> timeit shift(*args)
100 loops, best of 3: 4.28 ms per loop

這是一個替代的現場解決方案:

def movesec(l,srcIndex,n,dstIndex):
    if srcIndex+n>dstIndex: raise ValueError("overlapping indexes")
    for i in range(n):
        l.insert(dstIndex+1,l.pop(srcIndex))

    return l


print range(10)
print movesec(range(10),3,2,6)     

輸出:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]    # orginal
[0, 1, 2, 5, 6, 7, 3, 4, 8, 9]    # modified
>>> L = ['a','b','c','d','e','f','g','h']
>>> L[7:7] = L[2:5]
>>> L[2:5] = []
>>> L
['a', 'b', 'f', 'g', 'c', 'd', 'e', 'h']
def shift(L,start,n,i):
    return L[:start]+L[start+n:i]+L[start:start+n]+L[i:]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM