Jaki jest najskuteczniejszy sposób obracania listy w pythonie? Teraz mam coś takiego:
>>> 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]
Czy jest lepszy sposób?
rotate
to właściwe słowo, nieshift
.Odpowiedzi:
A
collections.deque
jest zoptymalizowany do ciągnięcia i pchania na obu końcach. Mają nawet dedykowanąrotate()
metodę.źródło
collections.deque rotate()
jest szybszy niż krojenie według wiki.python.org/moin/TimeComplexitydeque.rotate
wymagadeque
najpierw konwersji typu na obiekt, co jest wolniejsze niżl.append(l.pop(0))
. Więc jeśli masz na początek obiekt deque, to na pewno jest on najszybszy. W przeciwnym razie użyjl.append(l.pop(0))
.deque.rotate
jest O (k), ale konwersja typu z listy do deque to O (n) . Więc jeśli zaczynasz od listy, użycie deque.rotate to O (n) + O (k) = O (n).l.append(l.pop(0))
z drugiej strony jest O (1).A co z samym używaniem
pop(0)
?źródło
l.append(l.pop(0)
. Co, jeśli się nie mylę, to O (1).Numpy może to zrobić za pomocą
roll
polecenia:źródło
Zależy to od tego, co chcesz, aby to się stało:
Możesz zmienić swoje:
do:
źródło
Najprostszy sposób, w jaki mogę wymyślić:
źródło
collections.deque
jest szybsza, ale w przypadku najczęstszych przypadków długości listy na jednej iteracji lub w przypadku wielu iteracjia.append(a.pop(0))
będzie szybsza niż konwersja typu na dequeJeśli chcesz po prostu iterować te zestawy elementów zamiast tworzyć osobną strukturę danych, rozważ użycie iteratorów do skonstruowania wyrażenia generatora:
źródło
Zależy to również od tego, czy chcesz przesunąć listę na miejscu (mutując ją), czy też chcesz, aby funkcja zwróciła nową listę. Ponieważ według moich testów coś takiego jest co najmniej dwadzieścia razy szybsze niż implementacja, która dodaje dwie listy:
W rzeczywistości nawet dodanie
l = l[:]
do góry tego, aby działało na kopii przekazanej listy, jest jeszcze dwa razy szybsze.Różne implementacje z pewnym czasem na http://gist.github.com/288272
źródło
l[:n] = []
wybrałbymdel l[:n]
. Po prostu alternatywa.del
nadal jest instrukcją w Py3. Jednakx.__delitem__(y) <==> del x[y]
jeśli wolisz używać metod,l.__delitem__(slice(n))
jest również równoważny i działa zarówno w 2, jak i 3.Kilka uwag na temat czasu:
Jeśli zaczynasz od listy,
l.append(l.pop(0))
jest to najszybsza metoda, jakiej możesz użyć. Można to pokazać przy samej złożoności czasowej:Jeśli więc zaczynasz od
deque
obiektów, możesz to zrobićdeque.rotate()
kosztem O (k). Ale jeśli punktem początkowym jest lista, złożoność czasowa użyciadeque.rotate()
wynosi O (n).l.append(l.pop(0)
jest szybszy przy O (1).Dla ilustracji, oto kilka przykładowych czasów dla iteracji 1M:
Metody wymagające konwersji typu:
deque.rotate
z obiektem deque : 0,12380790710449219 sekund (najszybszy)deque.rotate
z konwersją typu: 6,853878974914551 sekundnp.roll
z nparray: 6.0491721630096436 sekundnp.roll
z konwersją typu: 27.558452129364014 sekundWymień metody wymienione tutaj:
l.append(l.pop(0))
: 0,32483696937561035 sekund (najszybszy)shiftInPlace
”: 4,819645881652832 sekundZastosowany kod czasowy znajduje się poniżej.
collections.deque
Pokazuje, że tworzenie deques z list to O (n):
Jeśli chcesz utworzyć obiekty deque:
Iteracje 1M @ 6.853878974914551 sekund
Jeśli masz już obiekty deque:
1M iteracje @ 0,12380790710449219 sekund
np.roll
Jeśli potrzebujesz stworzyć nparrays
1M iteracje @ 27.558452129364014 sekund
Jeśli masz już nparrays:
1M iteracji @ 6.0491721630096436 sekund
„Przesunięcie w miejscu”
Nie wymaga konwersji typu
1M iteracje @ 4,819645881652832 sekundy
l.append (l.pop (0))
Nie wymaga konwersji typu
1M iteracje @ 0,32483696937561035
źródło
l = [random.random() for i in range(100000)]
l.append(l.pop(0))
jest to najlepszy sposób na przesunięcie krótkich list (około 7 elementów) o jeden?l.append(l.pop(0))
jako odpowiedzi: To pytanie jest zamknięte jako duplikat. Może zagłosujesz na ponowne otwarcie?Zainteresowałem się tym i porównałem niektóre z sugerowanych rozwiązań z perfplot ( mój mały projekt).
Okazało się, że
jest zdecydowanie najszybszą metodą na małe zmiany
n
.Dla większych
n
,nie jest źle.
Zasadniczo perfplot wykonuje zmianę w celu zwiększenia dużych tablic i mierzy czas. Oto wyniki:
shift = 1
:shift = 100
:Kod do odtworzenia fabuły:
źródło
l.append(l.pop(0))
odpowiedzi: To pytanie jest zamknięte jako duplikat. Może zagłosujesz na ponowne otwarcie?Być może bardziej odpowiedni jest bufet pierścieniowy. To nie jest lista, chociaż jest prawdopodobne, że może zachowywać się wystarczająco jak lista do twoich celów.
Problem polega na tym, że wydajność przesunięcia na liście wynosi O (n), co staje się znaczące dla wystarczająco dużych list.
Przesunięcie bufora pierścieniowego oznacza po prostu aktualizację położenia głowy, którym jest O (1)
źródło
Dla niezmiennej implementacji możesz użyć czegoś takiego:
źródło
Jeśli twoim celem jest wydajność (cykli? Pamięci?), Możesz lepiej spojrzeć na moduł macierzy: http://docs.python.org/library/array.html
Tablice nie mają narzutu list.
Jeśli chodzi o czyste listy, to, co masz, jest tak dobre, jak możesz mieć nadzieję.
źródło
Myślę, że tego szukasz:
źródło
Inna alternatywa:
źródło
Przyjmuję ten model kosztów jako odniesienie:
http://scripts.mit.edu/~6.006/fall07/wiki/index.php?title=Python_Cost_Model
Metodą dzielenia listy i łączenia dwóch podlist są operacje liniowe. Sugerowałbym użycie popu, który jest operacją o stałym czasie, np .:
źródło
collections.dequeue
pop i appendleft, które są opcjami O (1). W mojej pierwszej odpowiedzi powyżej wstawiam O (n).collections.deque
Nie wiem, czy to jest „wydajne”, ale działa również:
EDYCJA: Witam ponownie, właśnie znalazłem duży problem z tym rozwiązaniem! Rozważ następujący kod:
Metoda shift_classlist () wykonuje ten sam kod, co mój x.insert (0, x.pop ()) - rozwiązanie, otherlist jest listą niezależną od klasy. Po przekazaniu zawartości otherlist do listy MyClass.classlist wywołanie shift_classlist () zmienia również listę otherlist:
WYJŚCIE KONSOLI:
Używam Python 2.7. Nie wiem, czy to błąd, ale myślę, że bardziej prawdopodobne jest, że coś tutaj nie zrozumiałem.
Czy ktoś z was wie, dlaczego tak się dzieje?
źródło
x.classlist = otherlist
powoduje, żex.classlist
odwołuje się do tej samej listy,otherlist
a następnie wywołaniex.shift_classlist()
powoduje mutację listy oraz ponieważ obie nazwy odnoszą się do tego samego obiektu listy. Obie nazwy wydają się zmieniać, ponieważ są tylko aliasami dla tego samego obiektu.x.classlist = otherlist[:]
Zamiast tego użyj, aby przypisać kopię listy.Następująca metoda to O (n) w miejscu ze stałą pamięcią pomocniczą:
Zauważ, że w Pythonie takie podejście jest okropnie nieefektywne w porównaniu do innych, ponieważ nie może korzystać z natywnych implementacji żadnego z elementów.
źródło
Mam podobne rzeczy. Na przykład, aby przesunąć o dwa ...
źródło
Myślę, że masz najbardziej efektywny sposób
źródło
Jaki jest przypadek użycia? Często nie potrzebujemy w pełni przesuniętej tablicy - wystarczy uzyskać dostęp do kilku elementów w przesuniętej tablicy.
Pobieranie wycinków w Pythonie to środowisko wykonawcze O (k), gdzie k jest wycinkiem, więc obrót w plasterkach to środowisko wykonawcze N. Polecenie odwrotnego obrotu to także O (k). Czy możemy zrobić lepiej?
Rozważmy tablicę, która jest bardzo duża (powiedzmy, że tak duża, że jej wycięcie byłoby wolniejsze obliczeniowo). Alternatywnym rozwiązaniem byłoby pozostawienie oryginalnej tablicy w spokoju i po prostu obliczenie indeksu elementu, który istniałby w naszym pożądanym indeksie po pewnym przesunięciu.
Dostęp do przesuniętego elementu staje się w ten sposób O (1).
źródło
Poniższa funkcja kopiuje listę wysłaną do szablonu, dzięki czemu funkcja pop nie wpływa na oryginalną listę:
Testowanie:
Wynik:
źródło
Jon Bentley w Programming Pearls (kolumna 2) opisuje elegancki i skuteczny algorytm obracania
n
wektora elementu-elementux
pozostawionego oi
pozycje:Można to przetłumaczyć na Python w następujący sposób:
Próbny:
źródło
Dla listy
X = ['a', 'b', 'c', 'd', 'e', 'f']
i pożądanej wartości przesunięciashift
mniejszej niż długość listy możemy zdefiniować funkcjęlist_shift()
jak poniżejPrzykłady
list_shift(X,1)
zwraca['b', 'c', 'd', 'e', 'f', 'a']
list_shift(X,3)
zwraca['d', 'e', 'f', 'a', 'b', 'c']
źródło
list_shift
w Twojej odpowiedzi jest identyczna z funkcjąshift
z pierwotnego pytania, więc nie jest to odpowiedź na pytanie: „Czy istnieje lepszy sposób?”Na przykład dane
funkcja powinna wrócić
[9, 7, 6, 3, 8]
. Wykonano trzy obroty:Podany inny przykład
funkcja powinna wrócić
[0, 0, 0]
Dany
funkcja powinna wrócić
[1, 2, 3, 4]
źródło
Szukałem na miejscu rozwiązania tego problemu. To rozwiązuje cel w O (k).
źródło
dla podobnej funkcjonalności jak zmiana w innych językach:
źródło
L.pop(0)