2012-04-06 15 views
49

Suppongo di avere una lista x con lunghezza non trovata da cui voglio inserire in modo casuale un elemento in modo che l'elenco non contenga più l'elemento in seguito. Qual è il modo più pioneristico di farlo?Qual è il modo più poderoso di far apparire un elemento casuale da un elenco?

posso farlo utilizzando un combincation piuttosto unhandy di pop, random.randint e len e vorrebbero vedere le soluzioni più brevi o più bello:

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

Edit: Quello che sto cercando di realizzare è consecutivamente pop elementi casuali da una lista. (Vale a dire, pop in modo casuale un elemento e spostarlo in un dizionario, pop in modo casuale un altro elemento e spostarlo in un altro dizionario, ...)


Nota che sto usando Python 2.6 e non ha trovato alcuna soluzione via la funzione di ricerca.

+3

Non sono un gran pitista, ma sicuramente mi sembra abbastanza buono. –

risposta

52

Cosa ti sembra di essere fino a non sembra molto Pythonic in primo luogo. Non dovresti rimuovere elementi dal centro di una lista, perché le liste sono implementate come matrici in tutte le implementazioni Python che conosco, quindi questa è un'operazione O(n).

Se si ha realmente bisogno di questa funzionalità come parte di un algoritmo, è necessario verificare una struttura dati come la blist che supporti l'eliminazione efficiente dal centro.

in puro Python, che cosa si può fare se non è necessario l'accesso ai restanti elementi è solo mischiare la lista e poi scorrere su di esso:

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

Se si ha realmente bisogno il resto (che è un po 'di un odore di codice, IMHO), almeno si può pop() dalla fine della lista ora (che è veloce!):

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

In generale, spesso è possibile esprimere i propri programmi più elegante se ne usi di più stile funzionale, invece di mutare (come fai con la lista).

+3

Quindi un'idea migliore (più veloce) sarebbe quella di usare 'random.shuffle (x)' e quindi 'x.pop()'? Non capisco come fare questo "funzionale"? – Henrik

+0

@ Henrik: Non so cosa stai cercando di fare, quindi non posso davvero dirlo. Dovresti aggiungere ulteriori informazioni alla domanda, o semplicemente commentare qui ciò che vuoi ottenere :) Questo sembra essere un caso del [problema XY] (http://meta.stackexchange.com/questions/66377/what-is -the-xy-problem) ... –

+0

Ho una lista di elementi da cui voglio schioccare consecutivamente elementi casuali. – Henrik

29

Non si ottiene molto di meglio, ma ecco un leggero miglioramento:

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

documentazione su random.randrange():

random.randrange ([start], stop [, step ])
Restituisce un elemento selezionato a caso da range(start, stop, step). Questo è equivalente a choice(range(start, stop, step)), ma in realtà non crea un oggetto intervallo.

3

Un modo per farlo è:

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

Questo potrebbe diventare problematico se gli elementi si verificano più volte. –

+2

Questo rimuoverà l'elemento più a sinistra quando ci sono duplicati, causando un risultato non perfettamente casuale. – FogleBird

+0

Con 'pop' puoi puntare un nome sull'elemento rimosso, con questo non puoi. – agf

8

Ecco un'altra alternativa: perché non mischiare l'elenco prima e quindi iniziare a scoppiare gli elementi fino a quando non rimangono più elementi?in questo modo:

import random 

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

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

Perché non 'for p in x'? –

+3

@NiklasB. perché stiamo rimuovendo elementi dalla lista. Se non è assolutamente necessario rimuovere elementi, sì sono d'accordo con te: '[per p in x]' –

+0

Perché altera la lista e se vuoi solo selezionare metà degli elementi ora e l'altra metà dopo, avrai il set rimanente dopo. – Henrik

7

Per rimuovere un singolo elementoall'indice caso da una lista se l'ordine del resto degli elementi della lista non importa:

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) 

Lo swap viene utilizzato per evitare O (n) comportamento sulla cancellazione da una parte centrale di un elenco.

2

Mentre non scoppia dalla lista, ho incontrato questa domanda su Google mentre cercavo di ottenere X elementi casuali da una lista senza duplicati. Ecco quello che ho poi usato:

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

Questo potrebbe essere leggermente inefficiente come si sta mischiare un intero elenco, ma solo con una piccola parte di esso, ma io non sono un esperto di ottimizzazione in modo da potrei sbagliarmi.

+1

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

1

Questa risposta viene cortesia di @niklas-b:

"Probabilmente si desidera usare qualcosa come pypi.python.org/pypi/blist"

Per citare il PYPI page: ... un tipo di elenco simile

con prestazioni asintotiche migliori e prestazioni simili su elenchi di piccole dimensioni

Il blist è una sostituzione drop-in per l'elenco Python che fornisce prestazioni migliori di durante la modifica di elenchi di grandi dimensioni. Il pacchetto Blist anche fornisce liste ordinate, ordinate, weaksortedlist, weaksortedset, sorteddict e tipi btuple.

si potrebbe supporre abbassato prestazioni sul accesso casuale/random fine corsa, in quanto è un "copia in scrittura" struttura di dati. Ciò viola molte ipotesi sui casi d'uso negli elenchi Python, , quindi usalo con cautela.

TUTTAVIA, se il tuo caso d'uso principale è fare qualcosa di strano e innaturale con una lista (come nell'esempio forzato dato da @OP, o il mio problema di coda-con-passaggio di Python 2.6 FIFO), allora questo adattarsi bene al conto.

0

So che questa è una vecchia questione, ma solo per amor di documentazione:

Se (la persona googling la stessa domanda) stanno facendo quello che penso che stai facendo, che sta selezionando il numero k di elementi in modo casuale da una lista (dove k < = len (yourlist)), ma assicurandosi che ogni elemento non venga mai selezionato più di una volta (= campionamento senza sostituzione), è possibile utilizzare random.sample come suggerisce @ jf-sebastian. Ma senza sapere di più sul caso d'uso, non so se questo è quello che ti serve.

Problemi correlati