2012-02-12 46 views
17

Cuando se quiere recorrer secuencialmente a través de una lista de números que va a escribir:iteración aleatoria en Python

for i in range(1000): 
    # do something with i 

Pero lo que si se quiere iterar sobre la lista de números de la gama (0..999) al azar? Existe una necesidad (en cada iteración) de elegir aleatoriamente el número que no se eligió en ninguna iteración previa y hay una necesidad de iterar sobre todos los números del rango (0,999).

¿Sabes cómo hacer eso (inteligente)?

Respuesta

23

Puede utilizar random.shuffle() para, así, baraja una lista:

import random 

r = list(range(1000)) 
random.shuffle(r) 
for i in r: 
    # do something with i 

Por cierto, en muchos casos en los que tendría que utilizar un bucle for en un rango de números enteros en otros lenguajes de programación, se puede describa directamente la "cosa" que desea iterar en Python.
Por ejemplo, si desea utilizar los valores de i acceder a los elementos de una lista, usted debe mezclar mejor la lista directamente:

lst = [1970, 1991, 2012] 
random.shuffle(lst) 
for x in lst: 
    print x 

NOTA: Hay que tener la siguiente advertencia en cuenta al utilizar random.shuffle() (tomado de la docs:

Tenga en cuenta que, incluso para bastante pequeña len (x), el número total de permutaciones de x es mayor que el período de más de números aleatorios gen eradores; esto implica que la mayoría de las permutaciones de una secuencia larga pueden nunca generarse.

+1

@ Greg: En realidad me di cuenta de que random.shuffle modifica el operando en el lugar, por lo que incluso me cant't utilizarlo como una expresión:/Gracias por la pista, sin embargo, he cambiado eso. –

+0

Sin preocupaciones, borré mi comentario porque ya no se aplica una vez que cambiaste eso. :) –

+3

Además, Python siembra automáticamente su generador de números aleatorios por lo que no se requiere una llamada a 'random.seed()'. –

3

utilizar el método de random.shuffle:

itrange = list(range(100)) 
random.shuffle(itrange) 
for i in itrange: 
    print i 
+1

Para una prueba a prueba de futuro, necesitaría usar 'list (rango (100))' en Python 3 ya que 'range' devuelve un iterador en 3.x. –

+0

Gracias, es correcto ahora. Tenga en cuenta que para las matrices largas no todas las permutaciones son posibles: http://docs.python.org/library/random.html#random.shuffle – Gregor

15

La gente a menudo pierden oportunidades para la modularización. Se puede definir una función para encapsular la idea de "iterar al azar":

def randomly(seq): 
    shuffled = list(seq) 
    random.shuffle(shuffled) 
    return iter(shuffled) 

a continuación:

for i in randomly(range(1000)): 
    #.. we're good to go .. 
+0

Gracias, este es el camino a seguir. +1 para la legibilidad! – shapecatcher

+1

Curioso: ¿Por qué devolver el iter en lugar de la lista? – Moberg

+2

No estoy seguro de por qué devolví el iter. Devolver una lista también debería estar bien. –

5

Demostrando generadores de Python y el Fisher–Yates shuffle.

import random 

def shuffled(sequence): 
    deck = list(sequence) 
    while len(deck): 
     i = random.randint(0, len(deck) - 1) # choose random card 
     card = deck[i]      # take the card 
     deck[i] = deck[-1]     # put top card in its place 
     deck.pop()       # remove top card 
     yield card 

Solo generará tantos números aleatorios como los utilice. Pero, sinceramente, probablemente no esté ahorrando mucho, por lo que normalmente debería usar random.shuffle.

Nota: Si se elige la tarjeta superior, deck[i] = deck.pop() no sería seguro, por lo que la eliminación de la parte superior se realiza en dos pasos.

+0

Esta es probablemente la respuesta más subestimada. Los otros (incluido el aceptado) todos convierten el iterable en una lista y mezclan eso. Esto funciona en la mayoría de los casos (simples), pero la memoria es ineficaz y mata su programa si su iterador es (muy) grande. Trabajar con productos cartesianos de listas largas, por ejemplo, es imposible con las otras soluciones. – Fred

+0

@Fred: ¿Huh? La primera línea de esta solución es 'deck = list (sequence)'. Puede ser capaz de salirse con la construcción de la lista perezosamente, pero es el mismo tiempo asintótico y memoria que los demás. Esta solución solo te permite generar solo tantos números aleatorios como necesites, y los números aleatorios son baratos. – leewz

+1

@Fred: si su iterable es muy grande, debería considerar retroceder algunos niveles. Por ejemplo, para obtener un pequeño subconjunto de un producto cartesiano de listas largas (de tamaño conocido), puede pretender que el producto es una lista aplanada, luego generar una lista aleatoria de índices distintos en esa lista de una manera u otra, que usted convertir a índices en cada lista de componentes. Estarías usando el hecho de que la fuente es un producto de listas. – leewz

3

Hay una función random.permutation() en numpy que hace exactamente eso por usted. Su código se vería así

from numpy.random import permutation 

for i in permutation(1000): 
    # do something with i 
+0

No lo confundas con itertools.permutations() aunque :) – Moberg