OPはこれをインプレースで実行したいと考えています。
操作を高速化するための鍵は、リストの作成とリストの短縮/延長を最小限に抑えることです。これは、リストインデックスの割り当てを常に1:1にするように努力する必要があることを意味します。したがって、noL[i:i] = L[a:b]
とnoL[a:b] = []
です。でループを使用するとinsert
、pop
リストが何度も短くなったり長くなったりするため、さらに悪いことになります。リストを連結することも悪いことです。最初にパーツごとに1つのリストを作成してから、各パーツに1つずつ、ますます大きな連結リストを作成する必要があるからです+
。これを「インプレース」で実行するため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 = start
、実行したい操作は、マーカーでカットし、とを交換することです。ただし、andが挿入位置である場合は、 andも交換します。したがって、この関数では、スワップする必要がある2つの連続するセグメントを処理するだけです。b = end = start + length
c
|
span1
span2
b = start
c = end
a
span1
span2
物を移動しながら重複する値を格納する必要があるため、新しいリストの作成を完全に回避することはできません。ただし、2つのスパンのどちらを一時リストに保存するかを選択することで、リストをできるだけ短くすることができます。
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
コスはタイミングに誤りがあったようですので、引数を修正(とend
から計算)した後、コードでそれらをやり直しました。これらは、最も遅いものから最も速いものへの結果です。start
length
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
他のアプローチのいずれかが正しい結果をもたらすことをテストしていません。