2012-04-06 5 views
49

Angenommen, ich habe eine Liste x mit unbekannter Länge, aus der ich nach dem Zufallsprinzip ein Element einfügen möchte, so dass die Liste das Element danach nicht enthält. Was ist der pythischste Weg, dies zu tun?Was ist die pythischste Art, ein zufälliges Element aus einer Liste zu entfernen?

Ich kann es eine ziemlich unhandliche combincation von pop verwenden, random.randint und len und mag Lösungen kürzer oder schöner sehen:

import random 
x = [1,2,3,4,5,6] 
x.pop(random.randint(0,len(x)-1)) 

Edit: Was ich versuche zu erreichen ist konsekutiv Pop zufällige Elemente aus einer Liste. (Dh zufällig ein Element Pop und es zu einem Wörterbuch bewegen, zufällig ein anderes Element Pop und in ein anderes Wörterbuch bewegen, ...)


Bitte beachte, dass ich verwende Python 2.6 und können keine Lösungen über die Suchfunktion.

+3

Ich bin kein großer Pythonista, aber das sieht mir ziemlich gut. –

Antwort

52

Was Sie scheinen bis zu sein, nicht sehr Pythonic in erster Linie sieht. Sie sollten Objekte nicht aus der Mitte einer Liste entfernen, da Listen in allen bekannten Python-Implementierungen als Arrays implementiert sind. Dies ist also eine O(n)-Operation. Wenn Sie diese Funktionalität wirklich als Teil eines Algorithmus benötigen, sollten Sie eine Datenstruktur wie blist auschecken, die effizientes Löschen von der Mitte unterstützt.

In reinen Python, was Sie tun können, wenn Sie Zugang brauchen nicht zu den übrigen Elementen ist nur die Liste mischen und dann über sie iterieren:

lst = [1,2,3] 
random.shuffle(lst) 
for x in lst: 
    # ... 

Wenn Sie wirklich brauchen der Rest (das ist ein bisschen ein Codegeruch, IMHO), zumindest können Sie pop() vom Ende der Liste jetzt (was schnell ist!):

while lst: 
    x = lst.pop() 
    # do something with the element  

In der Regel können Sie oft Ihre Programme ausdrücken mehr elegant wenn Sie mehr verwenden funktionaler Stil, anstatt muting Zustand (wie Sie mit der Liste tun).

+3

Eine bessere (schnellere) Idee wäre also 'random.shuffle (x)' und dann 'x.pop()'? Ich verstehe nicht, wie man das "funktional" macht? – Henrik

+0

@ Henrik: Ich weiß nicht, was du versuchst zu tun, also kann ich es nicht wirklich sagen. Sie sollten der Frage weitere Informationen hinzufügen oder hier nur kommentieren, was Sie erreichen möchten :) Dies scheint ein Fall des [XY-Problems] zu sein (http://meta.stackexchange.com/questions/66377/what-is) -the-xy-problem) ... –

+0

Ich habe eine Liste von Elementen, aus denen ich nacheinander zufällige Elemente einfügen möchte. – Henrik

29

Sie werden nicht viel besser als das, aber hier ist eine leichte Verbesserung:

x.pop(random.randrange(len(x))) 

Dokumentation auf random.randrange():

random.randrange ([Start], stoppen [, Schritt ])
Gibt ein zufällig ausgewähltes Element aus range(start, stop, step) zurück. Dies entspricht choice(range(start, stop, step)), erstellt jedoch kein Bereichsobjekt.

3

Eine Möglichkeit, es zu tun ist:

x.remove(random.choice(x)) 
+6

Dies könnte problematisch werden, wenn Elemente mehr als einmal vorkommen. –

+2

Dadurch wird das Element ganz links entfernt, wenn Duplikate vorhanden sind, was zu einem nicht vollkommen zufälligen Ergebnis führt. – FogleBird

+0

Mit 'pop' können Sie einen Namen auf das entfernte Element zeigen, mit dem können Sie nicht. – agf

8

Hier ist eine andere Alternative: Warum mischen Sie nicht die Liste zuerst, und starten Sie dann Elemente davon zu knallen, bis keine Elemente mehr übrig bleiben?dies wie:

import random 

x = [1,2,3,4,5,6] 
random.shuffle(x) 

while x: 
    p = x.pop() 
    # do your stuff with p 
+1

Warum nicht 'für p in x'? –

+3

@ NiklasB. weil wir Elemente aus der Liste entfernen. Wenn es nicht absolut notwendig ist, Elemente zu entfernen, ja stimme ich dir zu: '[für p in x]' –

+0

Weil es die Liste ändert und wenn Sie nur die Hälfte der Elemente jetzt und die andere Hälfte später auswählen möchten, haben Sie der Rest wird später eingestellt. – Henrik

7

Um ein einzelnes Element zufällig Index aus einer Liste, wenn die Reihenfolge der übrigen Listenelemente spielt keine Rolle zu entfernen:

import random 

L = [1,2,3,4,5,6] 
i = random.randrange(len(L)) # get random index 
L[i], L[-1] = L[-1], L[i] # swap with the last element 
x = L.pop()     # pop last element O(1) 

Der Swap verwendet wird, zu vermeiden, O (n) Verhalten beim Löschen von einer Mitte einer Liste.

2

Während ich nicht aus der Liste platzt, bin ich auf diese Frage bei Google gestoßen, als ich versuchte, X zufällige Einträge aus einer Liste ohne Duplikate zu erhalten. Hier ist, was ich schließlich verwendet:

items = [1, 2, 3, 4, 5] 
items_needed = 2 
from random import shuffle 
shuffle(items) 
for item in items[:items_needed]: 
    print(item) 

Diese etwas ineffizient sein kann, wenn Sie eine ganze Liste ist schlurfende aber nur einen kleinen Teil davon, aber ich bin kein Experte Optimierung, also könnte ich falsch sein.

+1

'random.sample (items, items_needed)' – jfs

1

Diese Antwort kommt mit freundlicher Genehmigung von @niklas-b:

"Sie wollen wahrscheinlich etwas wie pypi.python.org/pypi/blist verwenden"

die PYPI page zu zitieren:

... eine Liste artiger Typ mit besserer asymptotischer Leistung und ähnlichem Leistung auf kleinen Listen

Die Blist ist ein Drop-In-Ersatz für die Python-Liste, der bessere Leistung bietet, wenn große Listen geändert werden. Das Blist-Paket bietet auch Sortedlist, Sortedset, Weaksortedlist, Weaksortedset, Sorteddict und Btuple-Typen.

Man würde annehmen, Leistung auf dem Random Access/random Laufenden abgesenkt, da es ein "Copy on Write" Datenstruktur ist. Dies verstößt gegen viele Anwendungsfallannahmen auf Python-Listen, , also verwenden Sie es vorsichtig.

ABER, wenn Ihr Hauptanwendungsfall ist, etwas Seltsames und Unnatürliches mit einer Liste zu tun (wie in dem erzwungenen Beispiel von @OP oder meinem Python 2.6 FIFO Queue-mit-Pass-Over-Problem), dann wird dies passen Sie die Rechnung gut an.

0

Ich weiß, dass dies eine alte Frage, aber nur für die Dokumentation willen:

Wenn Sie (die Person, die gleiche Frage googeln) tun, was ich denke, Sie tun, was k Anzahl der Elemente ist die Auswahl zufällig aus eine Liste (wo k < = len (yourlist)), aber sicherstellen, dass jedes Element nie mehr als einmal ausgewählt wird (= Sampling ohne Ersatz), könnten Sie random.sample wie @ jf-sebastian schlägt. Aber ohne mehr über den Anwendungsfall zu wissen, weiß ich nicht, ob Sie das brauchen.

Verwandte Themen