Asumo PO quiere hacer esto in-situ.
La clave para realizar una operación rápida es minimizar la creación de listas y el acortamiento/alargamiento de listas. Esto significa que debemos esforzarnos por realizar siempre una asignación 1: 1 de índices de listas, de modo que no L[i:i] = L[a:b]
y no L[a:b] = []
. El uso de bucles con insert
y pop
es aún peor, porque acorta y alarga la lista muchas veces. Las listas de concatenación también son malas porque primero tiene que crear una lista para cada parte y luego crear listas concatenadas cada vez más grandes, una para cada +
. Como quiera hacer esto "in situ", deberá asignar la lista generada al final al 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
Permite hacer una primera observación. Si a = start
, b = end = start + length
y c
es la posición de inserción, entonces la operación que queremos hacer es cortar en los marcadores |
y cambiar span1
y span2
.Pero si b = start
y c = end
y a
es la posición de inserción, entonces nosotros también queremos cambiar span1
y span2
. Entonces, en nuestra función, solo tratamos con dos segmentos consecutivos que deben intercambiarse.
No podemos evitar totalmente hacer nuevas listas, porque necesitamos almacenar valores superpuestos mientras movemos cosas. Nos puede sin embargo, hacer la lista lo más breve posible, eligiendo cuál de los dos tramos para almacenar en una lista temporal.
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
Kos parece haber cometido un error en sus tiempos, por lo que les hizo de nuevo con su código después de corregir los argumentos (el cálculo de end
start
y length
), y estos son los resultados, desde la más lenta a la más rápida.
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
No he probado que ninguno de los otros enfoques arroje resultados correctos.
¿Cómo sabe que c, d, e son los elementos que desee mover? ¿Están en posición fija o están revisando valores? –
¿Qué sucede si la sublista solapa el punto de inserción? –
Esto no es posible, la respuesta de @ lazyr también lo impide. – TTT