46

When you want to iterate sequentially over a list of numbers you will write:

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

But what if you want to iterate over the list of numbers from the range (0..999) randomly? There is a need (in every iteration) to choose randomly the number that wasn't chosen in any previous iteration and there is a need to iterate over all of the numbers from the range (0..999).

Do you know how to do that (smart)?

xralf
  • 3,312
  • 45
  • 129
  • 200

6 Answers6

51

You can use random.shuffle() to, well, shuffle a list:

import random

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

By the way, in many cases where you'd use a for loop over a range of integers in other programming languages, you can directly describe the "thing" you want to iterate in Python.
For example, if you want to use the values of i to access elements of a list, you should better shuffle the list directly:

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

NOTE: You should bear the following warning in mind when using random.shuffle() (taken from the docs:

Note that for even rather small len(x), the total number of permutations of x is larger than the period of most random number generators; this implies that most permutations of a long sequence can never be generated.

Drew McGowen
  • 11,471
  • 1
  • 31
  • 57
Niklas B.
  • 92,950
  • 18
  • 194
  • 224
  • 1
    @Greg: Actually I noticed that random.shuffle modifies the operand in place, so I cant't even use it as an expression :/ Thanks for the hint, though, I changed it. – Niklas B. Feb 12 '12 at 20:31
  • No worries, I deleted my comment because it no longer applied once you changed that. :) – Greg Hewgill Feb 12 '12 at 20:32
  • 3
    Also, Python automatically seeds its random number generator so a call to `random.seed()` is not required. – Greg Hewgill Feb 12 '12 at 20:33
31

Here's a different approach to iterating a list in random order. This doesn't modify the original list unlike the solutions that use shuffle()

lst=['a','b','c','d','e','f']
for value in sorted(lst,key=lambda _: random.random()):
    print value

or:

for value in random.sample(lst,len(lst)):
    print value
James Scriven
  • 7,784
  • 1
  • 32
  • 36
  • 2
    Highly underrated solution which doesn't require creating a temporary copy of the list for shuffling, if the original list needs to be preserved - and doesn't require invoking numpy (although numpy's permutation is also a decent solution). +1 – ZKA Dec 30 '19 at 16:13
20

People often miss opportunities for modularization. You can define a function to encapsulate the idea of "iterate randomly":

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

then:

for i in randomly(range(1000)):
    #.. we're good to go ..
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
10

There is a function random.permutation() in numpy that does exactly that for you. Your code would look like

from numpy.random import permutation

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

Demonstrating Python generators and the 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

You only generate as many random numbers as you use. But honestly, it's probably not saving much, so you should usually use random.shuffle.

Note: If the top card is chosen, deck[i] = deck.pop() would not be safe, so removing the top is done in two steps.

leewz
  • 3,201
  • 1
  • 18
  • 38
  • 2
    This is probably a most underrated answer. The other ones (including the accepted one) all turn the iterable into a list and shuffle that. This works in most (simple) cases, but is memory inefficient and kills your program if your iterator is (very) large. Working with cartesian products of long lists, for example, is impossible with the other solutions. – Fred Sep 19 '16 at 15:26
  • 1
    @Fred: Huh? This solution's first line is `deck = list(sequence)`. You might be able to get away with lazily building the list, but it's the same asymptotic time and memory as the others. This solution just lets you generate only as many random numbers as you need, and random numbers are cheap. – leewz Sep 20 '16 at 02:57
  • 1
    @Fred: If your iterable is very large, you should consider stepping back a few levels. For example, to get a small subset of a cartesian product of long (known-sized) lists, you can pretend the product is a flattened list, then generate a random list of distinct indices into that list in one way or another, which you convert to indices into each component list. You'd be using the fact that the source is a product of lists. – leewz Sep 20 '16 at 03:10
  • Yes, you are obviously completely right. I don't know what got into me when I wrote that answer. Good hint on using the flattened list! – Fred Sep 23 '16 at 14:51
  • @Fred To be clear, it wouldn't be an actual list, but a "pretend" flattened list, which creates elements as they're requested. You would not need to store any elements. – leewz Sep 29 '16 at 00:06
3

Use the random.shuffle method:

itrange = list(range(100))
random.shuffle(itrange)
for i in itrange:
    print i
Gregor
  • 4,306
  • 1
  • 22
  • 37
  • 2
    To future-proof this answer, you would need to use `list(range(100))` in Python 3 since `range` returns an iterator in 3.x. – Greg Hewgill Feb 12 '12 at 20:31
  • Thanks, it's correct now. Please note, that for long arrays not all permutations are possible: http://docs.python.org/library/random.html#random.shuffle – Gregor Feb 12 '12 at 20:33