Was ist der effizienteste Weg, um eine Liste in Python zu drehen? Im Moment habe ich so etwas:
>>> 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]
Gibt es einen besseren Weg?
rotate
ist das richtige Wort, nichtshift
.Antworten:
A
collections.deque
ist für das Ziehen und Drücken an beiden Enden optimiert. Sie haben sogar eine speziellerotate()
Methode.quelle
collections.deque rotate()
ist schneller als das Schneiden gemäß wiki.python.org/moin/TimeComplexitydeque.rotate
eine Typkonvertierung in eindeque
Objekt erforderlich ist , die langsamer ist alsl.append(l.pop(0))
. Wenn Sie also zunächst ein Deque-Objekt haben, stellen Sie sicher, dass es am schnellsten ist. Andernfalls verwenden Siel.append(l.pop(0))
.deque.rotate
ist O (k), aber die Typkonvertierung von Liste zu Deque ist O (n) . Wenn Sie also mit einer Liste beginnen, ist die Verwendung von deque.rotate O (n) + O (k) = O (n).l.append(l.pop(0))
auf der anderen Seite ist O (1).Was ist mit nur zu verwenden
pop(0)
?quelle
l.append(l.pop(0)
. Was, wenn ich mich nicht irre, O (1) ist.Numpy kann dies mit dem folgenden
roll
Befehl tun :quelle
Es hängt davon ab, was passieren soll, wenn Sie dies tun:
Vielleicht möchten Sie Folgendes ändern:
zu:
quelle
Der einfachste Weg, den ich mir vorstellen kann:
quelle
collections.deque
ist schneller, aber für die meisten Fälle vona.append(a.pop(0))
Wenn Sie nur über diese Elementmengen iterieren möchten, anstatt eine separate Datenstruktur zu erstellen, sollten Sie Iteratoren verwenden, um einen Generatorausdruck zu erstellen:
quelle
Dies hängt auch davon ab, ob Sie die Liste verschieben (mutieren) möchten oder ob die Funktion eine neue Liste zurückgeben soll. Denn nach meinen Tests ist so etwas mindestens zwanzigmal schneller als Ihre Implementierung, die zwei Listen hinzufügt:
Tatsächlich ist das Hinzufügen eines
l = l[:]
oben, um eine Kopie der übergebenen Liste zu bearbeiten, immer noch doppelt so schnell.Verschiedene Implementierungen mit etwas Timing unter http://gist.github.com/288272
quelle
l[:n] = []
würde ich gehendel l[:n]
. Nur eine Alternative.del
ist immer noch eine Aussage in Py3. Allerdingsx.__delitem__(y) <==> del x[y]
, wenn Sie also mit Methoden bevorzugen,l.__delitem__(slice(n))
ist auch gleichwertig und arbeitet sowohl in der 2 & 3Nur ein paar Hinweise zum Timing:
Wenn Sie mit einer Liste beginnen,
l.append(l.pop(0))
ist dies die schnellste Methode, die Sie verwenden können. Dies kann allein mit zeitlicher Komplexität gezeigt werden:Wenn Sie also mit
deque
Objekten beginnen, können Sie diesdeque.rotate()
auf Kosten von O (k) tun. Wenn der Ausgangspunkt jedoch eine Liste ist, beträgt die zeitliche Komplexität der Verwendungdeque.rotate()
O (n).l.append(l.pop(0)
ist bei O (1) schneller.Zur Veranschaulichung sind hier einige Beispiel-Timings für 1M-Iterationen aufgeführt:
Methoden, die eine Typkonvertierung erfordern:
deque.rotate
mit Deque-Objekt: 0,12380790710449219 Sekunden (am schnellsten)deque.rotate
mit Typkonvertierung: 6.853878974914551 Sekundennp.roll
mit nparray: 6.0491721630096436 Sekundennp.roll
mit Typkonvertierung: 27.558452129364014 SekundenHier erwähnte Methoden auflisten:
l.append(l.pop(0))
: 0,32483696937561035 Sekunden (am schnellsten)shiftInPlace
": 4.819645881652832 SekundenDer verwendete Timing-Code ist unten.
collection.deque
Zeigen, dass das Erstellen von Deques aus Listen O (n) ist:
Wenn Sie Deque-Objekte erstellen müssen:
1M Iterationen @ 6.853878974914551 Sekunden
Wenn Sie bereits Deque-Objekte haben:
1 Million Iterationen bei 0,12380790710449219 Sekunden
np.roll
Wenn Sie nparrays erstellen müssen
1M Iterationen @ 27.558452129364014 Sekunden
Wenn Sie bereits nparrays haben:
1M Iterationen @ 6.0491721630096436 Sekunden
"Verschieben an Ort und Stelle"
Erfordert keine Typkonvertierung
1M Iterationen @ 4.819645881652832 Sekunden
l.append (l.pop (0))
Erfordert keine Typkonvertierung
1M Iterationen @ 0.32483696937561035
quelle
l = [random.random() for i in range(100000)]
l.append(l.pop(0))
die beste Leistung ist, um kurze Listen (ca. 7 Elemente) um eins zu verschieben?l.append(l.pop(0))
als Antwort: Diese Frage wird als Duplikat geschlossen. Vielleicht stimmen Sie für die Wiedereröffnung?Ich habe mich auch dafür interessiert und einige der vorgeschlagenen Lösungen mit perfplot (einem kleinen Projekt von mir) verglichen .
Es stellt sich heraus, dass
ist bei weitem die schnellste Methode für kleine Schichten
n
.Für größere
n
,ist nicht schlecht
Im Wesentlichen führt Perfplot die Verschiebung zum Erhöhen großer Arrays durch und misst die Zeit. Hier sind die Ergebnisse:
shift = 1
::shift = 100
::Code zur Reproduktion der Handlung:
quelle
l.append(l.pop(0))
als Antwort: Diese Frage wird als Duplikat geschlossen. Vielleicht stimmen Sie für die Wiedereröffnung?Möglicherweise ist ein Ringpuffer besser geeignet. Es ist keine Liste, obwohl es wahrscheinlich ist, dass sie sich für Ihre Zwecke wie eine Liste verhält.
Das Problem ist, dass die Effizienz einer Verschiebung in einer Liste O (n) ist, was für ausreichend große Listen signifikant wird.
Das Verschieben in einen Ringpuffer aktualisiert einfach die Kopfposition, die O (1) ist.
quelle
Für eine unveränderliche Implementierung können Sie Folgendes verwenden:
quelle
Wenn Effizienz Ihr Ziel ist (Zyklen? Speicher?), Ist es möglicherweise besser, sich das Array-Modul anzusehen: anzusehen http://docs.python.org/library/array.html
Arrays haben nicht den Overhead von Listen.
Was reine Listen angeht, ist das, was Sie haben, so gut, wie Sie es sich erhoffen können.
quelle
Ich denke du suchst das:
quelle
Eine andere Alternative:
quelle
Ich nehme dieses Kostenmodell als Referenz:
http://scripts.mit.edu/~6.006/fall07/wiki/index.php?title=Python_Cost_Model
Ihre Methode zum Aufteilen der Liste und zum Verketten von zwei Unterlisten sind Operationen mit linearer Zeit. Ich würde vorschlagen, Pop zu verwenden, was eine Operation mit konstanter Zeit ist, z.
quelle
collections.dequeue
pop und appendleft, die beide O (1) ops sind. In meiner ersten Antwort oben ist Einfügen O (n).collections.deque
Ich weiß nicht, ob dies "effizient" ist, aber es funktioniert auch:
EDIT: Hallo nochmal, ich habe gerade ein großes Problem mit dieser Lösung gefunden! Betrachten Sie den folgenden Code:
Die Methode shift_classlist () führt denselben Code aus wie meine x.insert (0, x.pop ()) - Lösung. Otherlist ist eine von der Klasse unabhängige Liste. Nachdem Sie den Inhalt der anderen Liste an die Liste MyClass.classlist übergeben haben, ändert das Aufrufen von shift_classlist () auch die Liste der anderen Listen:
KONSOLE AUSGABE:
Ich benutze Python 2.7. Ich weiß nicht, ob das ein Fehler ist, aber ich denke, es ist wahrscheinlicher, dass ich hier etwas falsch verstanden habe.
Weiß jemand von euch, warum das passiert?
quelle
x.classlist = otherlist
dassx.classlist
sich Marken auf dieselbe Liste beziehenotherlist
und beim Aufrufenx.shift_classlist()
die Liste mutiert und beide Namen auf dasselbe Listenobjekt verweisen. Beide Namen scheinen sich zu ändern, da sie nur Aliase für dasselbe Objekt sind. Verwenden Siex.classlist = otherlist[:]
stattdessen, um eine Kopie der Liste zuzuweisen.Die folgende Methode ist O (n) mit konstantem Hilfsspeicher:
Beachten Sie, dass dieser Ansatz in Python im Vergleich zu anderen schrecklich ineffizient ist, da er die nativen Implementierungen der einzelnen Teile nicht nutzen kann.
quelle
Ich habe ähnliche Sache. Zum Beispiel um zwei zu verschieben ...
quelle
Ich denke, Sie haben den effizientesten Weg
quelle
Was ist der Anwendungsfall? Oft brauchen wir kein vollständig verschobenes Array - wir müssen nur auf eine Handvoll Elemente im verschobenen Array zugreifen.
Das Abrufen von Python-Slices ist die Laufzeit O (k), wobei k das Slice ist. Eine geschnittene Rotation ist also die Laufzeit N. Der Deque-Rotationsbefehl lautet ebenfalls O (k). Können wir es besser machen?
Stellen Sie sich ein Array vor, das extrem groß ist (sagen wir, es ist so groß, dass es rechnerisch langsam wäre, es in Scheiben zu schneiden). Eine alternative Lösung wäre, das ursprüngliche Array in Ruhe zu lassen und einfach den Index des Elements zu berechnen, das nach einer Verschiebung in unserem gewünschten Index vorhanden gewesen wäre.
Der Zugriff auf ein verschobenes Element wird somit zu O (1).
quelle
Die folgende Funktion kopiert die gesendete Liste in eine Vorlagenliste, sodass die Popup-Funktion die ursprüngliche Liste nicht beeinflusst:
Testen:
Ausgabe:
quelle
Jon Bentley beschreibt in Programming Pearls (Spalte 2) einen eleganten und effizienten Algorithmus zum Drehen eines von Positionen hinterlassenen
n
Elementvektors :x
i
Dies kann wie folgt in Python übersetzt werden:
Demo:
quelle
Für eine Liste
X = ['a', 'b', 'c', 'd', 'e', 'f']
und einen gewünschten Verschiebungswert vonshift
weniger als der Listenlänge können wir die Funktionlist_shift()
wie folgt definierenBeispiele,
list_shift(X,1)
gibt['b', 'c', 'd', 'e', 'f', 'a']
list_shift(X,3)
zurück gibt zurück['d', 'e', 'f', 'a', 'b', 'c']
quelle
list_shift
in Ihrer Antwort ist identisch mit der Funktionshift
in der ursprünglichen Frage, daher ist dies keine Antwort auf die eigentliche Frage: "Gibt es einen besseren Weg?"Zum Beispiel gegeben
Die Funktion sollte zurückkehren
[9, 7, 6, 3, 8]
. Es wurden drei Umdrehungen gemacht:Für ein anderes Beispiel gegeben
Die Funktion sollte zurückkehren
[0, 0, 0]
Gegeben
Die Funktion sollte zurückkehren
[1, 2, 3, 4]
quelle
Ich suchte nach einer Lösung für dieses Problem. Dies löst den Zweck in O (k).
quelle
für ähnliche Funktionen wie die Verschiebung in anderen Sprachen:
quelle
L.pop(0)