fastest method to choose a random element
Arnaud Delobelle
arnodel at googlemail.com
Sat Jan 5 16:34:15 EST 2008
On Jan 5, 8:50 pm, Paul Hankin <paul.han... at gmail.com> wrote:
> On Jan 5, 5:12 pm, Paul Hankin <paul.han... at gmail.com> wrote:
>
>
>
> > On Jan 5, 4:14 pm, c... at mailinator.com wrote:
>
> > > On Jan 5, 5:07 pm, c... at mailinator.com wrote:
>
> > > > Hello, Paul and Arnaud.
> > > > While I think about your answers: do you think there is any way to
> > > > avoid shuffle?
> > > > It may take unnecessary long on a long list most of whose elements
> > > > have the property.
>
> > You could generate a random shuffle of range(len(seq)) lazily, and use
> > that to iterate over your sequence.
>
> > import random
> > import itertools
>
> > def randxrange(n):
> > "Generate the numbers 0 to n-1 in a random order"
> > x = range(n)
> > for i in xrange(n):
> > r = random.randrange(n - i)
> > yield x[r]
> > x[r] = x[n - i - 1]
>
> > def shuffled(seq):
> > "Generate the elements of seq in a random order"
> > return (seq[i] for i in randxrange(len(seq)))
>
> > def pick_random(seq, prop):
> > return itertools.ifilter(prop, shuffled(seq)).next()
>
> At the risk of filling this thread with my posts, here's a much
> simplified and faster version of this code that uses no extra storage.
>
> import random
>
> def pick_random(seq, prop):
> L = len(seq)
> for i in xrange(L):
> r = random.randrange(L - i)
> if prop(seq[r]):
> return seq[r]
> seq[r] = seq[L - i - 1]
>
> --
> Paul Hankin
Great! This is so simple it must be the best way to do it. Thanks
for that: I could feel there must be an elegant way, but I couldn't
put my finger on it. The only downside, of course, is that it mutates
seq. Moreover, it can be slightly modified to be a generator (in case
one wants to draw many elements from the sequence), improving vastly
on the generator function I proposed earlier.
def pick_random(seq, prop):
for i in xrange(len(seq), 0, -1):
r = random.randrange(i)
if prop(seq[r]):
return seq[r]
seq[r] = seq[i - 1]
# Generator version.
def random_picker(seq, prop):
for i in xrange(len(seq), 0, -1):
while True:
r = random.randrange(i)
if not prop(seq[r]): break
yield seq[r]
seq[r] = seq[i - 1]
--
Arnaud
More information about the Python-list
mailing list