Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e360d95

Browse files
committed
The combo of getstate/setstate/jumpahead is very powerful, but needs
examples to flesh it out for the uninitiated. Here they are.
1 parent 85e2e47 commit e360d95

2 files changed

Lines changed: 93 additions & 8 deletions

File tree

Doc/lib/librandom.tex

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,57 @@ \section{\module{random} ---
3838
This is especially useful for multi-threaded programs, creating a different
3939
instance of \var{Random} for each thread, and using the \method{jumpahead()}
4040
method to ensure that the generated sequences seen by each thread don't
41-
overlap. Class \var{Random} can also be subclassed if you want to use a
42-
different basic generator of your own devising: in that case, override the
41+
overlap (see example below).
42+
Class \var{Random} can also be subclassed if you want to use a different
43+
basic generator of your own devising: in that case, override the
4344
\method{random()}, \method{seed()}, \method{getstate()},
4445
\method{setstate()} and \method{jumpahead()} methods.
4546

47+
Here's one way to create threadsafe distinct and non-overlapping generators:
48+
49+
\begin{verbatim}
50+
def create_generators(num, delta, firstseed=None):
51+
"""Return list of num distinct generators.
52+
Each generator has its own unique segment of delta elements
53+
from Random.random()'s full period.
54+
Seed the first generator with optional arg firstseed (default
55+
is None, to seed from current time).
56+
"""
57+
58+
from random import Random
59+
g = Random(firstseed)
60+
result = [g]
61+
for i in range(num - 1):
62+
laststate = g.getstate()
63+
g = Random()
64+
g.setstate(laststate)
65+
g.jumpahead(delta)
66+
result.append(g)
67+
return result
68+
69+
gens = create_generators(10, 1000000)
70+
\end{verbatim}
71+
72+
That creates 10 distinct generators, which can be passed out to 10 distinct
73+
threads. The generators don't share state so can be called safely in
74+
parallel. So long as no thread calls its \code{g.random()} more than a
75+
million times (the second argument to \function{create_generators}), the
76+
sequences seen by each thread will not overlap. The period of the
77+
underlying Wichmann-Hill generator limits how far this technique can be
78+
pushed.
79+
80+
Just for fun, note that since we know the period, \method{jumpahead()} can
81+
also be used to "move backward in time":
82+
83+
\begin{verbatim}
84+
>>> g = Random(42) # arbitrary
85+
>>> g.random()
86+
0.24855401895528142
87+
>>> g.jumpahead(6953607871644L - 1) # move *back* one
88+
>>> g.random()
89+
0.24855401895528142
90+
\end{verbatim}
91+
4692

4793
Bookkeeping functions:
4894

Lib/random.py

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,51 @@
2525
2626
Translated from anonymously contributed C/C++ source.
2727
28-
Multi-threading note: the random number generator used here is not
29-
thread-safe; it is possible that two calls return the same random
30-
value. But you can instantiate a different instance of Random() in
31-
each thread to get generators that don't share state, then use
32-
.setstate() and .jumpahead() to move the generators to disjoint
33-
segments of the full period.
28+
Multi-threading note: the random number generator used here is not thread-
29+
safe; it is possible that two calls return the same random value. However,
30+
you can instantiate a different instance of Random() in each thread to get
31+
generators that don't share state, then use .setstate() and .jumpahead() to
32+
move the generators to disjoint segments of the full period. For example,
33+
34+
def create_generators(num, delta, firstseed=None):
35+
""\"Return list of num distinct generators.
36+
Each generator has its own unique segment of delta elements from
37+
Random.random()'s full period.
38+
Seed the first generator with optional arg firstseed (default is
39+
None, to seed from current time).
40+
""\"
41+
42+
from random import Random
43+
g = Random(firstseed)
44+
result = [g]
45+
for i in range(num - 1):
46+
laststate = g.getstate()
47+
g = Random()
48+
g.setstate(laststate)
49+
g.jumpahead(delta)
50+
result.append(g)
51+
return result
52+
53+
gens = create_generators(10, 1000000)
54+
55+
That creates 10 distinct generators, which can be passed out to 10 distinct
56+
threads. The generators don't share state so can be called safely in
57+
parallel. So long as no thread calls its g.random() more than a million
58+
times (the second argument to create_generators), the sequences seen by
59+
each thread will not overlap.
60+
61+
The period of the underlying Wichmann-Hill generator is 6,953,607,871,644,
62+
and that limits how far this technique can be pushed.
63+
64+
Just for fun, note that since we know the period, .jumpahead() can also be
65+
used to "move backward in time":
66+
67+
>>> g = Random(42) # arbitrary
68+
>>> g.random()
69+
0.24855401895528142
70+
>>> g.jumpahead(6953607871644L - 1) # move *back* one
71+
>>> g.random()
72+
0.24855401895528142
3473
"""
3574
# XXX The docstring sucks.
3675

0 commit comments

Comments
 (0)