2012-04-06 14 views
49

Supongo que tengo una lista x con longitud desconocida desde la que quiero mostrar aleatoriamente un elemento para que la lista no contenga el elemento después. ¿Cuál es la forma más pitónica de hacer esto?¿Cuál es la forma más pitónica de sacar un elemento aleatorio de una lista?

puedo hacerlo usando un combincation más torpe de pop, random.randint y len y les gustaría ver soluciones más cortos o más agradables:

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

Editar: Lo que estoy tratando de lograr es consecutivamente pop elementos aleatorios de una lista. (Es decir, el pop al azar un elemento y moverlo a un diccionario, pop al azar otro elemento y moverlo a otro diccionario, ...)


Tenga en cuenta que estoy usando Python 2.6 y no encontrar ninguna solución a través la función de búsqueda.

+3

No soy mucho Pythonista, pero eso se ve bastante bien para mí. –

Respuesta

52

Lo que parece ser hasta no se ve muy Pythonic en el primer lugar. No debe eliminar cosas de la mitad de una lista, porque las listas se implementan como matrices en todas las implementaciones de Python que conozco, por lo que esta es una operación O(n).

Si realmente necesita esta funcionalidad como parte de un algoritmo, debe verificar una estructura de datos como blist que admita la eliminación eficiente desde el medio.

En Python puro, lo que puede hacer si no es necesario el acceso a los elementos restantes se acaba de barajar la lista primero y luego iterar sobre ella:

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

Si realmente necesita el resto (que es un poco de olor a código, en mi humilde opinión), al menos se puede pop() desde el final de la lista ahora (que es rápido!):

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

en general, a menudo se puede expresar sus programas más elegante si usas un poco más estilo funcional, en lugar de mutar el estado (como lo hace con la lista).

+3

Entonces, una idea mejor (más rápida) sería usar 'random.shuffle (x)' y luego 'x.pop()'? No entiendo cómo hacer esto "funcional"? – Henrik

+0

@Henrik: No sé lo que estás tratando de hacer, así que realmente no puedo decirlo. Debería agregar más información a la pregunta, o simplemente comentar aquí lo que quiere lograr :) Este parece ser un caso del [problema XY] (http://meta.stackexchange.com/questions/66377/what-is -the-xy-problem) ... –

+0

Tengo una lista de elementos de los que quiero separar aleatoriamente elementos aleatorios. – Henrik

29

Usted no recibirá mucho mejor que eso, pero aquí es una ligera mejora:

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

Documentación sobre random.randrange():

random.randrange ([Inicio], deje de [paso ])
Devuelve un elemento seleccionado al azar de range(start, stop, step). Esto es equivalente a choice(range(start, stop, step)), pero en realidad no crea un objeto de rango.

3

Una forma de hacerlo es:

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

Esto podría ser problemático si los elementos ocurren más una vez. –

+2

Esto eliminará el elemento situado más a la izquierda cuando haya duplicados, lo que provocará un resultado no totalmente aleatorio. – FogleBird

+0

Con 'pop' puedes señalar un nombre en el elemento eliminado, con esto no puedes. – agf

8

Aquí hay otra alternativa: ¿por qué no barajar la lista primero y, a continuación, iniciar elementos de la misma que hace estallar hasta que ya no siguen siendo elementos?como esta:

import random 

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

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

¿Por qué no 'for p in x'? –

+3

@NiklasB. porque estamos eliminando elementos de la lista. Si no es absolutamente necesario eliminar los elementos, sí, estoy de acuerdo con usted: '[for p in x]' –

+0

Porque altera la lista y si solo desea seleccionar la mitad de los elementos ahora y la otra mitad más tarde, tendrá el conjunto restante más tarde. – Henrik

7

Para eliminar un solo elemento en el índice azar de una lista si el orden del resto de elementos de la lista no 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) 

El intercambio se utiliza para evitar Comportamiento de O (n) al eliminar de la mitad de una lista.

2

Si bien no aparece en la lista, me encontré con esta pregunta en Google mientras trataba de obtener X elementos aleatorios de una lista sin duplicados. Esto es lo que finalmente utilicé:

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

Esto puede ser un poco ineficiente, ya que está barajando una lista completa pero sólo con una pequeña porción de él, pero no soy un experto optimización por lo que podría estar equivocado.

+1

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

1

Esta respuesta viene por cortesía de @niklas-b:

"Es posible que desee usar algo como pypi.python.org/pypi/blist"

citar el PYPI page: ... un tipo-lista como

con un mejor rendimiento asintótico y un rendimiento similar en en listas pequeñas

El blist es un reemplazo directo para la lista de Python que proporciona un mejor rendimiento cuando se modifican listas grandes. El paquete blist también proporciona sortedlist, sortedset, weaksortedlist, weaksortedset, sorteddict y tipos btuples.

es de suponer bajó el rendimiento en el acceso aleatorio/final de ejecución aleatoria, ya que es una "copia en escritura" estructura de datos. Esto viola muchas suposiciones de casos de uso en las listas de Python, , así que úselo con cuidado.

SIN EMBARGO, si su caso de uso principal es hacer algo raro y antinatural con una lista (como en el ejemplo forzado dado por @OP, o mi Python 2.6 FIFO queue-with-pass-over), entonces esto adaptarse a la factura muy bien.

0

Sé que esto es una vieja pregunta, pero sólo por el bien de la documentación:

Si (la persona googlear la misma pregunta) están haciendo lo que creo que está haciendo, que es la selección número k de elementos al azar de una lista (donde k < = len (su lista)), pero asegurándose de que cada elemento nunca se seleccione más de una vez (= muestreo sin reemplazo), puede usar random.sample como lo sugiere @ jf-sebastian. Pero sin saber más sobre el caso de uso, no sé si esto es lo que necesita.

Cuestiones relacionadas