Welcome, guest | Sign In | My Account | Store | Cart

iterate over all values of a range in random order.

related to http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/466177

Python, 44 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import array, random

TEADELTA     = 0x9e3779b9L
TEAROUNDS    = 32
TEASBOXSIZE  = 128
TEASBOXSHIFT = 7

class randrange(object):
    def __init__(self, start, stop):
        self.start = start
        self.max = stop - start
        self.sbox = array.array('I', [ random.randint(0, 0xffffffffL)
                                       for i in range(TEASBOXSIZE) ])
        bits = 0
        while (1 << bits) < self.max:
            bits += 1
        self.left = bits / 2
        self.right = bits - self.left
        self.mask = (1 << bits) - 1

        if TEASBOXSIZE < (1 << self.left):
            self.sboxmask = TEASBOXSIZE - 1
            self.kshift = TEASBOXSHIFT
        else:
            self.sboxmask = (1 << self.left) - 1
            self.kshift = self.left

    def __iter__(self):
        enc = 0
        for i in range(self.max):
            c = self.max
            while c >= self.max:
                c = enc
                enc += 1
                s = 0
                for j in range(TEAROUNDS):
                    s += TEADELTA
                    c ^= (self.sbox[(c ^ s) & self.sboxmask] << self.kshift)
                    c = (c + s) & self.mask
                    c = ((c << self.left) | (c >> self.right)) & self.mask
            yield self.start + c

    def __len__(self):
        return self.max

this implements a modified (variable block size) TEA cipher by Niels Provos . a similar version in C is available in the libdnet Python extension module (http://libdnet.sf.net/) - see dnet.rand.xrange()

3 comments

David Janes 15 years, 10 months ago  # | flag
import random

def randrange(start, stop):
    values = range(start, stop)

    while values:
        yield values.pop(random.randrange(len(values)))

    raise StopIteration

for x in randrange(0, 10):
    print x
Douglas Bagnall 15 years, 10 months ago  # | flag

it is quicker to use random.shuffle()

import random

def randrange(start, stop):
    values = range(start, stop)
    random.shuffle(values)

    while values:
        yield values.pop()

    raise StopIteration

for x in randrange(0, 10):
    print x
Dug Song (author) 15 years, 10 months ago  # | flag

hrr, i should have given an example... this is meant to handle cases where the results of range() will not fit in memory - e.g. a randomized walk over all values of the IP address space, or even sys.maxint, etc.