Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
8ff3d50e
Commit
8ff3d50e
authored
Dec 29, 2002
by
Raymond Hettinger
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
SF patch 658251: Install a C implementation of the Mersenne Twister as the
core generator for random.py.
parent
224ee46b
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
983 additions
and
322 deletions
+983
-322
Doc/lib/librandom.tex
Doc/lib/librandom.tex
+55
-83
Lib/random.py
Lib/random.py
+178
-225
Lib/test/test_random.py
Lib/test/test_random.py
+201
-14
Misc/NEWS
Misc/NEWS
+19
-0
Modules/_randommodule.c
Modules/_randommodule.c
+528
-0
setup.py
setup.py
+2
-0
No files found.
Doc/lib/librandom.tex
View file @
8ff3d50e
...
...
@@ -8,30 +8,26 @@
This module implements pseudo-random number generators for various
distributions.
For integers, uniform selection from a range.
For sequences, uniform selection of a random element, and a function to
generate a random permutation of a list in-place.
For sequences, uniform selection of a random element, a function to
generate a random permutation of a list in-place, and a function for
random sampling without replacement.
On the real line, there are functions to compute uniform, normal (Gaussian),
lognormal, negative exponential, gamma, and beta distributions.
For generating distribution
of angles, the circular uniform and
von Mises distributions are
available.
For generating distribution
s of angles, the von Mises distribution
is
available.
Almost all module functions depend on the basic function
\function
{
random()
}
, which generates a random float uniformly in
the semi-open range [0.0, 1.0). Python uses the standard Wichmann-Hill
generator, combining three pure multiplicative congruential
generators of modulus 30269, 30307 and 30323. Its period (how many
numbers it generates before repeating the sequence exactly) is
6,953,607,871,644. While of much higher quality than the
\function
{
rand()
}
function supplied by most C libraries, the theoretical properties
are much the same as for a single linear congruential generator of
large modulus. It is not suitable for all purposes, and is completely
unsuitable for cryptographic purposes.
The functions in this module are not threadsafe: if you want to call these
functions from multiple threads, you should explicitly serialize the calls.
Else, because no critical sections are implemented internally, calls
from different threads may see the same return values.
the semi-open range [0.0, 1.0). Python uses the Mersenne Twister as
the core generator. It produces 53-bit precision floats and has a
period of 2**19937-1. The underlying implementation in C
is both fast and threadsafe. The Mersenne Twister is one of the most
extensively tested random number generators in existence. However, being
completely deterministic, it is not suitable for all purposes, and is
completely unsuitable for cryptographic purposes.
The functions supplied by this module are actually bound methods of a
hidden instance of the
\class
{
random.Random
}
class. You can
...
...
@@ -39,58 +35,19 @@ instantiate your own instances of \class{Random} to get generators
that don't share state. This is especially useful for multi-threaded
programs, creating a different instance of
\class
{
Random
}
for each
thread, and using the
\method
{
jumpahead()
}
method to ensure that the
generated sequences seen by each thread don't overlap (see example
below).
generated sequences seen by each thread don't overlap.
Class
\class
{
Random
}
can also be subclassed if you want to use a
different basic generator of your own devising: in that case, override
the
\method
{
random()
}
,
\method
{
seed()
}
,
\method
{
getstate()
}
,
\method
{
setstate()
}
and
\method
{
jumpahead()
}
methods.
Here's one way to create threadsafe distinct and non-overlapping generators:
\begin{verbatim}
def create
_
generators(num, delta, firstseed=None):
"""Return list of num distinct generators.
Each generator has its own unique segment of delta elements
from Random.random()'s full period.
Seed the first generator with optional arg firstseed (default
is None, to seed from current time).
"""
from random import Random
g = Random(firstseed)
result = [g]
for i in range(num - 1):
laststate = g.getstate()
g = Random()
g.setstate(laststate)
g.jumpahead(delta)
result.append(g)
return result
gens = create
_
generators(10, 1000000)
\end{verbatim}
That creates 10 distinct generators, which can be passed out to 10
distinct threads. The generators don't share state so can be called
safely in parallel. So long as no thread calls its
\code
{
g.random()
}
more than a million times (the second argument to
\function
{
create
_
generators()
}
, the sequences seen by each thread will
not overlap. The period of the underlying Wichmann-Hill generator
limits how far this technique can be pushed.
Just for fun, note that since we know the period,
\method
{
jumpahead()
}
can also be used to ``move backward in time:''
\begin{verbatim}
>>> g = Random(42) # arbitrary
>>> g.random()
0.25420336316883324
>>> g.jumpahead(6953607871644L - 1) # move *back* one
>>> g.random()
0.25420336316883324
\end{verbatim}
As an example of subclassing, the
\module
{
random
}
module provides
the
\class
{
WichmannHill
}
class which implements an alternative generator
in pure Python. The class provides a backward compatible way to
reproduce results from earlier versions of Python which used the
Wichmann-Hill algorithm as the core generator.
\versionchanged
[Substituted MersenneTwister for Wichmann-Hill]
{
2.3
}
Bookkeeping functions:
...
...
@@ -104,18 +61,6 @@ Bookkeeping functions:
If
\var
{
x
}
is not
\code
{
None
}
or an int or long,
\code
{
hash(
\var
{
x
}
)
}
is used instead.
If
\var
{
x
}
is an int or long,
\var
{
x
}
is used directly.
Distinct values between 0 and 27814431486575L inclusive are guaranteed
to yield distinct internal states (this guarantee is specific to the
default Wichmann-Hill generator, and may not apply to subclasses
supplying their own basic generator).
\end{funcdesc}
\begin{funcdesc}
{
whseed
}{
\optional
{
x
}}
This is obsolete, supplied for bit-level compatibility with versions
of Python prior to 2.1.
See
\function
{
seed
}
for details.
\function
{
whseed
}
does not guarantee
that distinct integer arguments yield distinct internal states, and can
yield no more than about 2**24 distinct internal states in all.
\end{funcdesc}
\begin{funcdesc}
{
getstate
}{}
...
...
@@ -134,17 +79,20 @@ Bookkeeping functions:
\end{funcdesc}
\begin{funcdesc}
{
jumpahead
}{
n
}
Change the internal state to
what it would be if
\function
{
random()
}
were called
\var
{
n
}
times, but do so quickly.
\var
{
n
}
is a
non-negative intege
r. This is most useful in multi-threaded
Change the internal state to
one different from and likely far away from
the current state.
\var
{
n
}
is a non-negative integer which is used to
scramble the current state vecto
r. This is most useful in multi-threaded
programs, in conjuction with multiple instances of the
\class
{
Random
}
class:
\method
{
setstate()
}
or
\method
{
seed()
}
can be used to force
all instances into the same internal state, and then
\method
{
jumpahead()
}
can be used to force the instances' states as
far apart as you like (up to the period of the generator).
class:
\method
{
setstate()
}
or
\method
{
seed()
}
can be used to force all
instances into the same internal state, and then
\method
{
jumpahead()
}
can be used to force the instances' states far apart.
\versionadded
{
2.1
}
\versionchanged
[Instead of jumping to a specific state,
\var
{
n
}
steps
ahead,
\method
{
jumpahead(
\var
{
n
}
)
}
jumps to another state likely to be
separated by many steps.]
{
2.3
}
\end{funcdesc}
Functions for integers:
\begin{funcdesc}
{
randrange
}{
\optional
{
start,
}
stop
\optional
{
, step
}}
...
...
@@ -279,8 +227,32 @@ these equations can be found in any statistics text.
\var
{
beta
}
is the shape parameter.
\end{funcdesc}
Alternative Generator
\begin{classdesc}
{
WichmannHill
}{
\optional
{
seed
}}
Class that implements the Wichmann-Hill algorithm as the core generator.
Has all of the same methods as
\class
{
Random
}
plus the
\method
{
whseed
}
method described below. Because this class is implemented in pure
Python, it is not threadsafe and may require locks between calls. The
period of the generator is 6,953,607,871,644 which is small enough to
require care that two independent random sequences do not overlap.
\end{classdesc}
\begin{funcdesc}
{
whseed
}{
\optional
{
x
}}
This is obsolete, supplied for bit-level compatibility with versions
of Python prior to 2.1.
See
\function
{
seed
}
for details.
\function
{
whseed
}
does not guarantee
that distinct integer arguments yield distinct internal states, and can
yield no more than about 2**24 distinct internal states in all.
\end{funcdesc}
\begin{seealso}
\seetext
{
M. Matsumoto and T. Nishimura, ``Mersenne Twister: A
623-dimensionally equidistributed uniform pseudorandom
number generator'',
\citetitle
{
ACM Transactions on Modeling and Computer Simulation
}
Vol. 8, No. 1, January pp.3-30 1998.
}
\seetext
{
Wichmann, B. A.
\&
Hill, I. D., ``Algorithm AS 183:
An efficient and portable pseudo-random number generator'',
\citetitle
{
Applied Statistics
}
31 (1982) 188-190.
}
...
...
Lib/random.py
View file @
8ff3d50e
...
...
@@ -18,61 +18,26 @@
negative exponential
gamma
beta
pareto
Weibull
distributions on the circle (angles 0 to 2pi)
---------------------------------------------
circular uniform
von Mises
Translated from anonymously contributed C/C++ source.
Multi-threading note: the random number generator used here is not thread-
safe; it is possible that two calls return the same random value. However,
you can instantiate a different instance of Random() in each thread to get
generators that don't share state, then use .setstate() and .jumpahead() to
move the generators to disjoint segments of the full period. For example,
def create_generators(num, delta, firstseed=None):
""
\
"
Return list of num distinct generators.
Each generator has its own unique segment of delta elements from
Random.random()'s full period.
Seed the first generator with optional arg firstseed (default is
None, to seed from current time).
""
\
"
from random import Random
g = Random(firstseed)
result = [g]
for i in range(num - 1):
laststate = g.getstate()
g = Random()
g.setstate(laststate)
g.jumpahead(delta)
result.append(g)
return result
gens = create_generators(10, 1000000)
That creates 10 distinct generators, which can be passed out to 10 distinct
threads. The generators don't share state so can be called safely in
parallel. So long as no thread calls its g.random() more than a million
times (the second argument to create_generators), the sequences seen by
each thread will not overlap.
The period of the underlying Wichmann-Hill generator is 6,953,607,871,644,
and that limits how far this technique can be pushed.
Just for fun, note that since we know the period, .jumpahead() can also be
used to "move backward in time":
>>> g = Random(42) # arbitrary
>>> g.random()
0.25420336316883324
>>> g.jumpahead(6953607871644L - 1) # move *back* one
>>> g.random()
0.25420336316883324
General notes on the underlying Mersenne Twister core generator:
* The period is 2**19937-1.
* It is one of the most extensively tested generators in existence
* Without a direct way to compute N steps forward, the
semantics of jumpahead(n) are weakened to simply jump
to another distant state and rely on the large period
to avoid overlapping sequences.
* The random() method is implemented in C, executes in
a single Python step, and is, therefore, threadsafe.
"""
# XXX The docstring sucks.
from
math
import
log
as
_log
,
exp
as
_exp
,
pi
as
_pi
,
e
as
_e
from
math
import
sqrt
as
_sqrt
,
acos
as
_acos
,
cos
as
_cos
,
sin
as
_sin
...
...
@@ -82,32 +47,20 @@ __all__ = ["Random","seed","random","uniform","randint","choice","sample",
"randrange"
,
"shuffle"
,
"normalvariate"
,
"lognormvariate"
,
"cunifvariate"
,
"expovariate"
,
"vonmisesvariate"
,
"gammavariate"
,
"stdgamma"
,
"gauss"
,
"betavariate"
,
"paretovariate"
,
"weibullvariate"
,
"getstate"
,
"setstate"
,
"jumpahead"
,
"whseed"
]
def
_verify
(
name
,
computed
,
expected
):
if
abs
(
computed
-
expected
)
>
1e-7
:
raise
ValueError
(
"computed value for %s deviates too much "
"(computed %g, expected %g)"
%
(
name
,
computed
,
expected
))
"getstate"
,
"setstate"
,
"jumpahead"
]
NV_MAGICCONST
=
4
*
_exp
(
-
0.5
)
/
_sqrt
(
2.0
)
_verify
(
'NV_MAGICCONST'
,
NV_MAGICCONST
,
1.71552776992141
)
TWOPI
=
2.0
*
_pi
_verify
(
'TWOPI'
,
TWOPI
,
6.28318530718
)
LOG4
=
_log
(
4.0
)
_verify
(
'LOG4'
,
LOG4
,
1.38629436111989
)
SG_MAGICCONST
=
1.0
+
_log
(
4.5
)
_verify
(
'SG_MAGICCONST'
,
SG_MAGICCONST
,
2.50407739677627
)
del
_verify
# Translated by Guido van Rossum from C source provided by
# Adrian Baddeley.
# Adrian Baddeley. Adapted by Raymond Hettinger for use with
# the Mersenne Twister core generator.
class
Random
:
from
_random
import
Random
as
CoreGenerator
class
Random
(
CoreGenerator
):
"""Random number generator base class used by bound module functions.
Used to instantiate instances of Random to get generators that don't
...
...
@@ -122,7 +75,7 @@ class Random:
"""
VERSION
=
1
# used by getstate/setstate
VERSION
=
2
# used by getstate/setstate
def
__init__
(
self
,
x
=
None
):
"""Initialize an instance.
...
...
@@ -131,12 +84,7 @@ class Random:
"""
self
.
seed
(
x
)
## -------------------- core generator -------------------
# Specific to Wichmann-Hill generator. Subclasses wishing to use a
# different core generator should override the seed(), random(),
# getstate(), setstate() and jumpahead() methods.
self
.
gauss_next
=
None
def
seed
(
self
,
a
=
None
):
"""Initialize internal state from hashable object.
...
...
@@ -144,141 +92,26 @@ class Random:
None or no argument seeds from current time.
If a is not None or an int or long, hash(a) is used instead.
If a is an int or long, a is used directly. Distinct values between
0 and 27814431486575L inclusive are guaranteed to yield distinct
internal states (this guarantee is specific to the default
Wichmann-Hill generator).
"""
if
a
is
None
:
# Initialize from current time
import
time
a
=
long
(
time
.
time
()
*
256
)
if
type
(
a
)
not
in
(
type
(
3
),
type
(
3L
)):
a
=
hash
(
a
)
a
,
x
=
divmod
(
a
,
30268
)
a
,
y
=
divmod
(
a
,
30306
)
a
,
z
=
divmod
(
a
,
30322
)
self
.
_seed
=
int
(
x
)
+
1
,
int
(
y
)
+
1
,
int
(
z
)
+
1
CoreGenerator
.
seed
(
self
,
a
)
self
.
gauss_next
=
None
def
random
(
self
):
"""Get the next random number in the range [0.0, 1.0)."""
# Wichman-Hill random number generator.
#
# Wichmann, B. A. & Hill, I. D. (1982)
# Algorithm AS 183:
# An efficient and portable pseudo-random number generator
# Applied Statistics 31 (1982) 188-190
#
# see also:
# Correction to Algorithm AS 183
# Applied Statistics 33 (1984) 123
#
# McLeod, A. I. (1985)
# A remark on Algorithm AS 183
# Applied Statistics 34 (1985),198-200
# This part is thread-unsafe:
# BEGIN CRITICAL SECTION
x
,
y
,
z
=
self
.
_seed
x
=
(
171
*
x
)
%
30269
y
=
(
172
*
y
)
%
30307
z
=
(
170
*
z
)
%
30323
self
.
_seed
=
x
,
y
,
z
# END CRITICAL SECTION
# Note: on a platform using IEEE-754 double arithmetic, this can
# never return 0.0 (asserted by Tim; proof too long for a comment).
return
(
x
/
30269.0
+
y
/
30307.0
+
z
/
30323.0
)
%
1.0
def
getstate
(
self
):
"""Return internal state; can be passed to setstate() later."""
return
self
.
VERSION
,
self
.
_seed
,
self
.
gauss_next
return
self
.
VERSION
,
CoreGenerator
.
getstate
(
self
)
,
self
.
gauss_next
def
setstate
(
self
,
state
):
"""Restore internal state from object returned by getstate()."""
version
=
state
[
0
]
if
version
==
1
:
version
,
self
.
_seed
,
self
.
gauss_next
=
state
if
version
==
2
:
version
,
internalstate
,
self
.
gauss_next
=
state
CoreGenerator
.
setstate
(
self
,
internalstate
)
else
:
raise
ValueError
(
"state with version %s passed to "
"Random.setstate() of version %s"
%
(
version
,
self
.
VERSION
))
def
jumpahead
(
self
,
n
):
"""Act as if n calls to random() were made, but quickly.
n is an int, greater than or equal to 0.
Example use: If you have 2 threads and know that each will
consume no more than a million random numbers, create two Random
objects r1 and r2, then do
r2.setstate(r1.getstate())
r2.jumpahead(1000000)
Then r1 and r2 will use guaranteed-disjoint segments of the full
period.
"""
if
not
n
>=
0
:
raise
ValueError
(
"n must be >= 0"
)
x
,
y
,
z
=
self
.
_seed
x
=
int
(
x
*
pow
(
171
,
n
,
30269
))
%
30269
y
=
int
(
y
*
pow
(
172
,
n
,
30307
))
%
30307
z
=
int
(
z
*
pow
(
170
,
n
,
30323
))
%
30323
self
.
_seed
=
x
,
y
,
z
def
__whseed
(
self
,
x
=
0
,
y
=
0
,
z
=
0
):
"""Set the Wichmann-Hill seed from (x, y, z).
These must be integers in the range [0, 256).
"""
if
not
type
(
x
)
==
type
(
y
)
==
type
(
z
)
==
int
:
raise
TypeError
(
'seeds must be integers'
)
if
not
(
0
<=
x
<
256
and
0
<=
y
<
256
and
0
<=
z
<
256
):
raise
ValueError
(
'seeds must be in range(0, 256)'
)
if
0
==
x
==
y
==
z
:
# Initialize from current time
import
time
t
=
long
(
time
.
time
()
*
256
)
t
=
int
((
t
&
0xffffff
)
^
(
t
>>
24
))
t
,
x
=
divmod
(
t
,
256
)
t
,
y
=
divmod
(
t
,
256
)
t
,
z
=
divmod
(
t
,
256
)
# Zero is a poor seed, so substitute 1
self
.
_seed
=
(
x
or
1
,
y
or
1
,
z
or
1
)
self
.
gauss_next
=
None
def
whseed
(
self
,
a
=
None
):
"""Seed from hashable object's hash code.
None or no argument seeds from current time. It is not guaranteed
that objects with distinct hash codes lead to distinct internal
states.
This is obsolete, provided for compatibility with the seed routine
used prior to Python 2.1. Use the .seed() method instead.
"""
if
a
is
None
:
self
.
__whseed
()
return
a
=
hash
(
a
)
a
,
x
=
divmod
(
a
,
256
)
a
,
y
=
divmod
(
a
,
256
)
a
,
z
=
divmod
(
a
,
256
)
x
=
(
x
+
a
)
%
256
or
1
y
=
(
y
+
a
)
%
256
or
1
z
=
(
z
+
a
)
%
256
or
1
self
.
__whseed
(
x
,
y
,
z
)
## ---- Methods below this point do not need to be overridden when
## ---- subclassing for the purpose of using a different core generator.
...
...
@@ -744,6 +577,153 @@ class Random:
u
=
self
.
random
()
return
alpha
*
pow
(
-
_log
(
u
),
1.0
/
beta
)
## -------------------- Wichmann-Hill -------------------
class
WichmannHill
(
Random
):
VERSION
=
1
# used by getstate/setstate
def
seed
(
self
,
a
=
None
):
"""Initialize internal state from hashable object.
None or no argument seeds from current time.
If a is not None or an int or long, hash(a) is used instead.
If a is an int or long, a is used directly. Distinct values between
0 and 27814431486575L inclusive are guaranteed to yield distinct
internal states (this guarantee is specific to the default
Wichmann-Hill generator).
"""
if
a
is
None
:
# Initialize from current time
import
time
a
=
long
(
time
.
time
()
*
256
)
if
not
isinstance
(
a
,
(
int
,
long
)):
a
=
hash
(
a
)
a
,
x
=
divmod
(
a
,
30268
)
a
,
y
=
divmod
(
a
,
30306
)
a
,
z
=
divmod
(
a
,
30322
)
self
.
_seed
=
int
(
x
)
+
1
,
int
(
y
)
+
1
,
int
(
z
)
+
1
self
.
gauss_next
=
None
def
random
(
self
):
"""Get the next random number in the range [0.0, 1.0)."""
# Wichman-Hill random number generator.
#
# Wichmann, B. A. & Hill, I. D. (1982)
# Algorithm AS 183:
# An efficient and portable pseudo-random number generator
# Applied Statistics 31 (1982) 188-190
#
# see also:
# Correction to Algorithm AS 183
# Applied Statistics 33 (1984) 123
#
# McLeod, A. I. (1985)
# A remark on Algorithm AS 183
# Applied Statistics 34 (1985),198-200
# This part is thread-unsafe:
# BEGIN CRITICAL SECTION
x
,
y
,
z
=
self
.
_seed
x
=
(
171
*
x
)
%
30269
y
=
(
172
*
y
)
%
30307
z
=
(
170
*
z
)
%
30323
self
.
_seed
=
x
,
y
,
z
# END CRITICAL SECTION
# Note: on a platform using IEEE-754 double arithmetic, this can
# never return 0.0 (asserted by Tim; proof too long for a comment).
return
(
x
/
30269.0
+
y
/
30307.0
+
z
/
30323.0
)
%
1.0
def
getstate
(
self
):
"""Return internal state; can be passed to setstate() later."""
return
self
.
VERSION
,
self
.
_seed
,
self
.
gauss_next
def
setstate
(
self
,
state
):
"""Restore internal state from object returned by getstate()."""
version
=
state
[
0
]
if
version
==
1
:
version
,
self
.
_seed
,
self
.
gauss_next
=
state
else
:
raise
ValueError
(
"state with version %s passed to "
"Random.setstate() of version %s"
%
(
version
,
self
.
VERSION
))
def
jumpahead
(
self
,
n
):
"""Act as if n calls to random() were made, but quickly.
n is an int, greater than or equal to 0.
Example use: If you have 2 threads and know that each will
consume no more than a million random numbers, create two Random
objects r1 and r2, then do
r2.setstate(r1.getstate())
r2.jumpahead(1000000)
Then r1 and r2 will use guaranteed-disjoint segments of the full
period.
"""
if
not
n
>=
0
:
raise
ValueError
(
"n must be >= 0"
)
x
,
y
,
z
=
self
.
_seed
x
=
int
(
x
*
pow
(
171
,
n
,
30269
))
%
30269
y
=
int
(
y
*
pow
(
172
,
n
,
30307
))
%
30307
z
=
int
(
z
*
pow
(
170
,
n
,
30323
))
%
30323
self
.
_seed
=
x
,
y
,
z
def
__whseed
(
self
,
x
=
0
,
y
=
0
,
z
=
0
):
"""Set the Wichmann-Hill seed from (x, y, z).
These must be integers in the range [0, 256).
"""
if
not
type
(
x
)
==
type
(
y
)
==
type
(
z
)
==
int
:
raise
TypeError
(
'seeds must be integers'
)
if
not
(
0
<=
x
<
256
and
0
<=
y
<
256
and
0
<=
z
<
256
):
raise
ValueError
(
'seeds must be in range(0, 256)'
)
if
0
==
x
==
y
==
z
:
# Initialize from current time
import
time
t
=
long
(
time
.
time
()
*
256
)
t
=
int
((
t
&
0xffffff
)
^
(
t
>>
24
))
t
,
x
=
divmod
(
t
,
256
)
t
,
y
=
divmod
(
t
,
256
)
t
,
z
=
divmod
(
t
,
256
)
# Zero is a poor seed, so substitute 1
self
.
_seed
=
(
x
or
1
,
y
or
1
,
z
or
1
)
self
.
gauss_next
=
None
def
whseed
(
self
,
a
=
None
):
"""Seed from hashable object's hash code.
None or no argument seeds from current time. It is not guaranteed
that objects with distinct hash codes lead to distinct internal
states.
This is obsolete, provided for compatibility with the seed routine
used prior to Python 2.1. Use the .seed() method instead.
"""
if
a
is
None
:
self
.
__whseed
()
return
a
=
hash
(
a
)
a
,
x
=
divmod
(
a
,
256
)
a
,
y
=
divmod
(
a
,
256
)
a
,
z
=
divmod
(
a
,
256
)
x
=
(
x
+
a
)
%
256
or
1
y
=
(
y
+
a
)
%
256
or
1
z
=
(
z
+
a
)
%
256
or
1
self
.
__whseed
(
x
,
y
,
z
)
## -------------------- test program --------------------
def
_test_generator
(
n
,
funccall
):
...
...
@@ -768,25 +748,11 @@ def _test_generator(n, funccall):
print
'avg %g, stddev %g, min %g, max %g'
%
\
(
avg
,
stddev
,
smallest
,
largest
)
def
_test_sample
(
n
):
# For the entire allowable range of 0 <= k <= n, validate that
# the sample is of the correct length and contains only unique items
population
=
xrange
(
n
)
for
k
in
xrange
(
n
+
1
):
s
=
sample
(
population
,
k
)
uniq
=
dict
.
fromkeys
(
s
)
assert
len
(
uniq
)
==
len
(
s
)
==
k
assert
None
not
in
uniq
def
_sample_generator
(
n
,
k
):
# Return a fixed element from the sample. Validates random ordering.
return
sample
(
xrange
(
n
),
k
)[
k
//
2
]
def
_test
(
N
=
2000
):
print
'TWOPI ='
,
TWOPI
print
'LOG4 ='
,
LOG4
print
'NV_MAGICCONST ='
,
NV_MAGICCONST
print
'SG_MAGICCONST ='
,
SG_MAGICCONST
_test_generator
(
N
,
'random()'
)
_test_generator
(
N
,
'normalvariate(0.0, 1.0)'
)
_test_generator
(
N
,
'lognormvariate(0.0, 1.0)'
)
...
...
@@ -808,25 +774,13 @@ def _test(N=2000):
_test_generator
(
N
,
'weibullvariate(1.0, 1.0)'
)
_test_generator
(
N
,
'_sample_generator(50, 5)'
)
# expected s.d.: 14.4
_test_generator
(
N
,
'_sample_generator(50, 45)'
)
# expected s.d.: 14.4
_test_sample
(
500
)
# Test jumpahead.
s
=
getstate
()
jumpahead
(
N
)
r1
=
random
()
# now do it the slow way
setstate
(
s
)
for
i
in
range
(
N
):
random
()
r2
=
random
()
if
r1
!=
r2
:
raise
ValueError
(
"jumpahead test failed "
+
`(N, r1, r2)`
)
# Create one instance, seeded from current time, and export its methods
# as module-level functions. The functions are not threadsafe, and state
# is shared across all uses (both in the user's code and in the Python
# libraries), but that's fine for most programs and is easier for the
# casual user than making them instantiate their own Random() instance.
# as module-level functions. The functions share state across all uses
#(both in the user's code and in the Python libraries), but that's fine
# for most programs and is easier for the casual user than making them
# instantiate their own Random() instance.
_inst
=
Random
()
seed
=
_inst
.
seed
random
=
_inst
.
random
...
...
@@ -850,7 +804,6 @@ weibullvariate = _inst.weibullvariate
getstate
=
_inst
.
getstate
setstate
=
_inst
.
setstate
jumpahead
=
_inst
.
jumpahead
whseed
=
_inst
.
whseed
if
__name__
==
'__main__'
:
_test
()
Lib/test/test_random.py
View file @
8ff3d50e
from
test
import
test_support
#!/usr/bin/env python
import
unittest
import
random
import
time
from
test
import
test_support
class
TestBasicOps
(
unittest
.
TestCase
):
# Superclass with tests common to all generators.
# Subclasses must arrange for self.gen to retrieve the Random instance
# to be tested.
def
randomlist
(
self
,
n
):
"""Helper function to make a list of random numbers"""
return
[
self
.
gen
.
random
()
for
i
in
xrange
(
n
)]
def
test_autoseed
(
self
):
self
.
gen
.
seed
()
state1
=
self
.
gen
.
getstate
()
time
.
sleep
(
1
)
self
.
gen
.
seed
()
# diffent seeds at different times
state2
=
self
.
gen
.
getstate
()
self
.
assertNotEqual
(
state1
,
state2
)
def
test_saverestore
(
self
):
N
=
1000
self
.
gen
.
seed
()
state
=
self
.
gen
.
getstate
()
randseq
=
self
.
randomlist
(
N
)
self
.
gen
.
setstate
(
state
)
# should regenerate the same sequence
self
.
assertEqual
(
randseq
,
self
.
randomlist
(
N
))
def
test_seedargs
(
self
):
for
arg
in
[
None
,
0
,
0L
,
1
,
1L
,
-
1
,
-
1L
,
10
**
20
,
-
(
10
**
20
),
3.14
,
1
+
2j
,
'a'
,
tuple
(
'abc'
)]:
self
.
gen
.
seed
(
arg
)
for
arg
in
[
range
(
3
),
dict
(
one
=
1
)]:
self
.
assertRaises
(
TypeError
,
self
.
gen
.
seed
,
arg
)
def
test_jumpahead
(
self
):
self
.
gen
.
seed
()
state1
=
self
.
gen
.
getstate
()
self
.
gen
.
jumpahead
(
100
)
state2
=
self
.
gen
.
getstate
()
# s/b distinct from state1
self
.
assertNotEqual
(
state1
,
state2
)
self
.
gen
.
jumpahead
(
100
)
state3
=
self
.
gen
.
getstate
()
# s/b distinct from state2
self
.
assertNotEqual
(
state2
,
state3
)
self
.
assertRaises
(
TypeError
,
self
.
gen
.
jumpahead
)
# needs an arg
self
.
assertRaises
(
TypeError
,
self
.
gen
.
jumpahead
,
"ick"
)
# wrong type
self
.
assertRaises
(
TypeError
,
self
.
gen
.
jumpahead
,
2.3
)
# wrong type
self
.
assertRaises
(
TypeError
,
self
.
gen
.
jumpahead
,
2
,
3
)
# too many
def
test_sample
(
self
):
# For the entire allowable range of 0 <= k <= N, validate that
# the sample is of the correct length and contains only unique items
N
=
100
population
=
xrange
(
N
)
for
k
in
xrange
(
N
+
1
):
s
=
self
.
gen
.
sample
(
population
,
k
)
self
.
assertEqual
(
len
(
s
),
k
)
uniq
=
dict
.
fromkeys
(
s
)
self
.
assertEqual
(
len
(
uniq
),
k
)
self
.
failIf
(
None
in
uniq
)
def
test_gauss
(
self
):
# Ensure that the seed() method initializes all the hidden state. In
# particular, through 2.2.1 it failed to reset a piece of state used
# by (and only by) the .gauss() method.
for
seed
in
1
,
12
,
123
,
1234
,
12345
,
123456
,
654321
:
self
.
gen
.
seed
(
seed
)
x1
=
self
.
gen
.
random
()
y1
=
self
.
gen
.
gauss
(
0
,
1
)
self
.
gen
.
seed
(
seed
)
x2
=
self
.
gen
.
random
()
y2
=
self
.
gen
.
gauss
(
0
,
1
)
self
.
assertEqual
(
x1
,
x2
)
self
.
assertEqual
(
y1
,
y2
)
class
WichmannHill_TestBasicOps
(
TestBasicOps
):
gen
=
random
.
WichmannHill
()
def
test_strong_jumpahead
(
self
):
# tests that jumpahead(n) semantics correspond to n calls to random()
N
=
1000
s
=
self
.
gen
.
getstate
()
self
.
gen
.
jumpahead
(
N
)
r1
=
self
.
gen
.
random
()
# now do it the slow way
self
.
gen
.
setstate
(
s
)
for
i
in
xrange
(
N
):
self
.
gen
.
random
()
r2
=
self
.
gen
.
random
()
self
.
assertEqual
(
r1
,
r2
)
def
test_gauss_with_whseed
(
self
):
# Ensure that the seed() method initializes all the hidden state. In
# particular, through 2.2.1 it failed to reset a piece of state used
# by (and only by) the .gauss() method.
for
seed
in
1
,
12
,
123
,
1234
,
12345
,
123456
,
654321
:
self
.
gen
.
whseed
(
seed
)
x1
=
self
.
gen
.
random
()
y1
=
self
.
gen
.
gauss
(
0
,
1
)
self
.
gen
.
whseed
(
seed
)
x2
=
self
.
gen
.
random
()
y2
=
self
.
gen
.
gauss
(
0
,
1
)
self
.
assertEqual
(
x1
,
x2
)
self
.
assertEqual
(
y1
,
y2
)
class
MersenneTwister_TestBasicOps
(
TestBasicOps
):
gen
=
random
.
Random
()
def
test_referenceImplementation
(
self
):
# Compare the python implementation with results from the original
# code. Create 2000 53-bit precision random floats. Compare only
# the last ten entries to show that the independent implementations
# are tracking. Here is the main() function needed to create the
# list of expected random numbers:
# void main(void){
# int i;
# unsigned long init[4]={61731, 24903, 614, 42143}, length=4;
# init_by_array(init, length);
# for (i=0; i<2000; i++) {
# printf("%.15f ", genrand_res53());
# if (i%5==4) printf("\n");
# }
# }
expected
=
[
0.45839803073713259
,
0.86057815201978782
,
0.92848331726782152
,
0.35932681119782461
,
0.081823493762449573
,
0.14332226470169329
,
0.084297823823520024
,
0.53814864671831453
,
0.089215024911993401
,
0.78486196105372907
]
self
.
gen
.
seed
(
61731L
+
(
24903L
<<
32
)
+
(
614L
<<
64
)
+
(
42143L
<<
96
))
actual
=
self
.
randomlist
(
2000
)[
-
10
:]
for
a
,
e
in
zip
(
actual
,
expected
):
self
.
assertAlmostEqual
(
a
,
e
,
places
=
14
)
def
test_strong_reference_implementation
(
self
):
# Like test_referenceImplementation, but checks for exact bit-level
# equality. This should pass on any box where C double contains
# at least 53 bits of precision (the underlying algorithm suffers
# no rounding errors -- all results are exact).
from
math
import
ldexp
expected
=
[
0x0eab3258d2231f
L
,
0x1b89db315277a5
L
,
0x1db622a5518016
L
,
0x0b7f9af0d575bf
L
,
0x029e4c4db82240
L
,
0x04961892f5d673
L
,
0x02b291598e4589
L
,
0x11388382c15694
L
,
0x02dad977c9e1fe
L
,
0x191d96d4d334c6
L
]
self
.
gen
.
seed
(
61731L
+
(
24903L
<<
32
)
+
(
614L
<<
64
)
+
(
42143L
<<
96
))
actual
=
self
.
randomlist
(
2000
)[
-
10
:]
for
a
,
e
in
zip
(
actual
,
expected
):
self
.
assertEqual
(
long
(
ldexp
(
a
,
53
)),
e
)
def
test_long_seed
(
self
):
# This is most interesting to run in debug mode, just to make sure
# nothing blows up. Under the covers, a dynamically resized array
# is allocated, consuming space proportional to the number of bits
# in the seed. Unfortunately, that's a quadratic-time algorithm,
# so don't make this horribly big.
seed
=
(
1L
<<
(
10000
*
8
))
-
1
# about 10K bytes
self
.
gen
.
seed
(
seed
)
# Ensure that the seed() method initializes all the hidden state. In
# particular, through 2.2.1 it failed to reset a piece of state used by
# (and only by) the .gauss() method.
class
TestModule
(
unittest
.
TestCase
):
def
testMagicConstants
(
self
):
self
.
assertAlmostEqual
(
random
.
NV_MAGICCONST
,
1.71552776992141
)
self
.
assertAlmostEqual
(
random
.
TWOPI
,
6.28318530718
)
self
.
assertAlmostEqual
(
random
.
LOG4
,
1.38629436111989
)
self
.
assertAlmostEqual
(
random
.
SG_MAGICCONST
,
2.50407739677627
)
for
seed
in
1
,
12
,
123
,
1234
,
12345
,
123456
,
654321
:
for
seeder
in
random
.
seed
,
random
.
whseed
:
seeder
(
seed
)
x1
=
random
.
random
()
y1
=
random
.
gauss
(
0
,
1
)
def
test__all__
(
self
)
:
# tests validity but not completeness of the __all__ list
defined
=
dict
.
fromkeys
(
dir
(
random
)
)
for
entry
in
random
.
__all__
:
self
.
failUnless
(
entry
in
defined
)
seeder
(
seed
)
x2
=
random
.
random
()
y2
=
random
.
gauss
(
0
,
1
)
def
test_main
():
suite
=
unittest
.
TestSuite
()
for
testclass
in
(
WichmannHill_TestBasicOps
,
MersenneTwister_TestBasicOps
,
TestModule
):
suite
.
addTest
(
unittest
.
makeSuite
(
testclass
))
test_support
.
run_suite
(
suite
)
test_support
.
vereq
(
x1
,
x2
)
test_support
.
vereq
(
y1
,
y2
)
if
__name__
==
"__main__"
:
test_main
(
)
Misc/NEWS
View file @
8ff3d50e
...
...
@@ -545,6 +545,25 @@ Library
and
'stop'
arguments
so
long
as
each
is
in
the
range
of
Python
's
bounded integers.
- Thanks to Raymond Hettinger, random.random() now uses a new core
generator. The Mersenne Twister algorithm is implemented in C,
threadsafe, faster than the previous generator, has an astronomically
large period (2**19937-1), creates random floats to full 53-bit
precision, and may be the most widely tested random number generator
in existence.
The random.jumpahead(n) method has different semantics for the new
generator. Instead of jumping n steps ahead, it uses n and the
existing state to create a new state. This means that jumpahead()
continues to support multi-threaded code needing generators of
non-overlapping sequences. However, it will break code which relies
on jumpahead moving a specific number of steps forward.
The attributes random.whseed and random.__whseed have no meaning for
the new generator. Code using these attributes should switch to a
new class, random.WichmannHill which is provided for backward
compatibility and to make an alternate generator available.
- New "algorithms" module: heapq, implements a heap queue. Thanks to
Kevin O'
Connor
for
the
code
and
Fran
ç
ois
Pinard
for
an
entertaining
write
-
up
explaining
the
theory
and
practical
uses
of
heaps
.
...
...
Modules/_randommodule.c
0 → 100644
View file @
8ff3d50e
/* Random objects */
/* ------------------------------------------------------------------
The code in this module was based on a download from:
http://www.math.keio.ac.jp/~matumoto/MT2002/emt19937ar.html
It was modified in 2002 by Raymond Hettinger as follows:
* the principal computational lines untouched except for tabbing.
* renamed genrand_res53() to random_random() and wrapped
in python calling/return code.
* genrand_int32() and the helper functions, init_genrand()
and init_by_array(), were declared static, wrapped in
Python calling/return code. also, their global data
references were replaced with structure references.
* unused functions from the original were deleted.
new, original C python code was added to implement the
Random() interface.
The following are the verbatim comments from the original code:
A C-program for MT19937, with initialization improved 2002/1/26.
Coded by Takuji Nishimura and Makoto Matsumoto.
Before using, initialize the state by using init_genrand(seed)
or init_by_array(init_key, key_length).
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The names of its contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Any feedback is very welcome.
http://www.math.keio.ac.jp/matumoto/emt.html
email: matumoto@math.keio.ac.jp
*/
/* ---------------------------------------------------------------*/
#include "Python.h"
#include <time.h> // for seeding to current time
/* Period parameters -- These are all magic. Don't change. */
#define N 624
#define M 397
#define MATRIX_A 0x9908b0dfUL
/* constant vector a */
#define UPPER_MASK 0x80000000UL
/* most significant w-r bits */
#define LOWER_MASK 0x7fffffffUL
/* least significant r bits */
typedef
struct
{
PyObject_HEAD
unsigned
long
state
[
N
];
int
index
;
}
RandomObject
;
static
PyTypeObject
Random_Type
;
#define RandomObject_Check(v) ((v)->ob_type == &Random_Type)
/* Random methods */
/* generates a random number on [0,0xffffffff]-interval */
static
unsigned
long
genrand_int32
(
RandomObject
*
self
)
{
unsigned
long
y
;
static
unsigned
long
mag01
[
2
]
=
{
0x0UL
,
MATRIX_A
};
/* mag01[x] = x * MATRIX_A for x=0,1 */
unsigned
long
*
mt
;
mt
=
self
->
state
;
if
(
self
->
index
>=
N
)
{
/* generate N words at one time */
int
kk
;
for
(
kk
=
0
;
kk
<
N
-
M
;
kk
++
)
{
y
=
(
mt
[
kk
]
&
UPPER_MASK
)
|
(
mt
[
kk
+
1
]
&
LOWER_MASK
);
mt
[
kk
]
=
mt
[
kk
+
M
]
^
(
y
>>
1
)
^
mag01
[
y
&
0x1UL
];
}
for
(;
kk
<
N
-
1
;
kk
++
)
{
y
=
(
mt
[
kk
]
&
UPPER_MASK
)
|
(
mt
[
kk
+
1
]
&
LOWER_MASK
);
mt
[
kk
]
=
mt
[
kk
+
(
M
-
N
)]
^
(
y
>>
1
)
^
mag01
[
y
&
0x1UL
];
}
y
=
(
mt
[
N
-
1
]
&
UPPER_MASK
)
|
(
mt
[
0
]
&
LOWER_MASK
);
mt
[
N
-
1
]
=
mt
[
M
-
1
]
^
(
y
>>
1
)
^
mag01
[
y
&
0x1UL
];
self
->
index
=
0
;
}
y
=
mt
[
self
->
index
++
];
y
^=
(
y
>>
11
);
y
^=
(
y
<<
7
)
&
0x9d2c5680UL
;
y
^=
(
y
<<
15
)
&
0xefc60000UL
;
y
^=
(
y
>>
18
);
return
y
;
}
/* random_random is the function named genrand_res53 in the original code;
* generates a random number on [0,1) with 53-bit resolution; note that
* 9007199254740992 == 2**53; I assume they're spelling "/2**53" as
* multiply-by-reciprocal in the (likely vain) hope that the compiler will
* optimize the division away at compile-time. 67108864 is 2**26. In
* effect, a contains 27 random bits shifted left 26, and b fills in the
* lower 26 bits of the 53-bit numerator.
* The orginal code credited Isaku Wada for this algorithm, 2002/01/09.
*/
static
PyObject
*
random_random
(
RandomObject
*
self
)
{
unsigned
long
a
=
genrand_int32
(
self
)
>>
5
,
b
=
genrand_int32
(
self
)
>>
6
;
return
PyFloat_FromDouble
((
a
*
67108864
.
0
+
b
)
*
(
1
.
0
/
9007199254740992
.
0
));
}
/* initializes mt[N] with a seed */
static
void
init_genrand
(
RandomObject
*
self
,
unsigned
long
s
)
{
int
mti
;
unsigned
long
*
mt
;
mt
=
self
->
state
;
mt
[
0
]
=
s
&
0xffffffffUL
;
for
(
mti
=
1
;
mti
<
N
;
mti
++
)
{
mt
[
mti
]
=
(
1812433253UL
*
(
mt
[
mti
-
1
]
^
(
mt
[
mti
-
1
]
>>
30
))
+
mti
);
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
/* In the previous versions, MSBs of the seed affect */
/* only MSBs of the array mt[]. */
/* 2002/01/09 modified by Makoto Matsumoto */
mt
[
mti
]
&=
0xffffffffUL
;
/* for >32 bit machines */
}
self
->
index
=
mti
;
return
;
}
/* initialize by an array with array-length */
/* init_key is the array for initializing keys */
/* key_length is its length */
static
PyObject
*
init_by_array
(
RandomObject
*
self
,
unsigned
long
init_key
[],
unsigned
long
key_length
)
{
unsigned
int
i
,
j
,
k
;
/* was signed in the original code. RDH 12/16/2002 */
unsigned
long
*
mt
;
mt
=
self
->
state
;
init_genrand
(
self
,
19650218UL
);
i
=
1
;
j
=
0
;
k
=
(
N
>
key_length
?
N
:
key_length
);
for
(;
k
;
k
--
)
{
mt
[
i
]
=
(
mt
[
i
]
^
((
mt
[
i
-
1
]
^
(
mt
[
i
-
1
]
>>
30
))
*
1664525UL
))
+
init_key
[
j
]
+
j
;
/* non linear */
mt
[
i
]
&=
0xffffffffUL
;
/* for WORDSIZE > 32 machines */
i
++
;
j
++
;
if
(
i
>=
N
)
{
mt
[
0
]
=
mt
[
N
-
1
];
i
=
1
;
}
if
(
j
>=
key_length
)
j
=
0
;
}
for
(
k
=
N
-
1
;
k
;
k
--
)
{
mt
[
i
]
=
(
mt
[
i
]
^
((
mt
[
i
-
1
]
^
(
mt
[
i
-
1
]
>>
30
))
*
1566083941UL
))
-
i
;
/* non linear */
mt
[
i
]
&=
0xffffffffUL
;
/* for WORDSIZE > 32 machines */
i
++
;
if
(
i
>=
N
)
{
mt
[
0
]
=
mt
[
N
-
1
];
i
=
1
;
}
}
mt
[
0
]
=
0x80000000UL
;
/* MSB is 1; assuring non-zero initial array */
Py_INCREF
(
Py_None
);
return
Py_None
;
}
/*
* The rest is Python-specific code, neither part of, nor derived from, the
* Twister download.
*/
static
PyObject
*
random_seed
(
RandomObject
*
self
,
PyObject
*
args
)
{
PyObject
*
result
=
NULL
;
/* guilty until proved innocent */
PyObject
*
masklower
=
NULL
;
PyObject
*
thirtytwo
=
NULL
;
PyObject
*
n
=
NULL
;
unsigned
long
*
key
=
NULL
;
unsigned
long
keymax
;
/* # of allocated slots in key */
unsigned
long
keyused
;
/* # of used slots in key */
PyObject
*
arg
=
NULL
;
if
(
!
PyArg_UnpackTuple
(
args
,
"seed"
,
0
,
1
,
&
arg
))
return
NULL
;
if
(
arg
==
NULL
||
arg
==
Py_None
)
{
time_t
now
;
time
(
&
now
);
init_genrand
(
self
,
(
unsigned
long
)
now
);
Py_INCREF
(
Py_None
);
return
Py_None
;
}
/* If the arg is an int or long, use its absolute value; else use
* the absolute value of its hash code.
*/
if
(
PyInt_Check
(
arg
)
||
PyLong_Check
(
arg
))
n
=
PyNumber_Absolute
(
arg
);
else
{
long
hash
=
PyObject_Hash
(
arg
);
if
(
hash
==
-
1
)
goto
Done
;
n
=
PyLong_FromUnsignedLong
((
unsigned
long
)
hash
);
}
if
(
n
==
NULL
)
goto
Done
;
/* Now split n into 32-bit chunks, from the right. Each piece is
* stored into key, which has a capacity of keymax chunks, of which
* keyused are filled. Alas, the repeated shifting makes this a
* quadratic-time algorithm; we'd really like to use
* _PyLong_AsByteArray here, but then we'd have to break into the
* long representation to figure out how big an array was needed
* in advance.
*/
keymax
=
8
;
/* arbitrary; grows later if needed */
keyused
=
0
;
key
=
(
unsigned
long
*
)
PyMem_Malloc
(
keymax
*
sizeof
(
*
key
));
if
(
key
==
NULL
)
goto
Done
;
masklower
=
PyLong_FromUnsignedLong
(
0xffffffffU
);
if
(
masklower
==
NULL
)
goto
Done
;
thirtytwo
=
PyInt_FromLong
(
32L
);
if
(
thirtytwo
==
NULL
)
goto
Done
;
while
(
PyObject_IsTrue
(
n
))
{
PyObject
*
newn
;
PyObject
*
pychunk
;
unsigned
long
chunk
;
pychunk
=
PyNumber_And
(
n
,
masklower
);
if
(
pychunk
==
NULL
)
goto
Done
;
chunk
=
PyLong_AsUnsignedLong
(
pychunk
);
Py_DECREF
(
pychunk
);
if
(
chunk
==
(
unsigned
long
)
-
1
&&
PyErr_Occurred
())
goto
Done
;
newn
=
PyNumber_Rshift
(
n
,
thirtytwo
);
if
(
newn
==
NULL
)
goto
Done
;
Py_DECREF
(
n
);
n
=
newn
;
if
(
keyused
>=
keymax
)
{
unsigned
long
bigger
=
keymax
<<
1
;
if
((
bigger
>>
1
)
!=
keymax
)
{
PyErr_NoMemory
();
goto
Done
;
}
key
=
(
unsigned
long
*
)
PyMem_Realloc
(
key
,
bigger
*
sizeof
(
*
key
));
if
(
key
==
NULL
)
goto
Done
;
keymax
=
bigger
;
}
assert
(
keyused
<
keymax
);
key
[
keyused
++
]
=
chunk
;
}
if
(
keyused
==
0
)
key
[
keyused
++
]
=
0UL
;
result
=
init_by_array
(
self
,
key
,
keyused
);
Done:
Py_XDECREF
(
masklower
);
Py_XDECREF
(
thirtytwo
);
Py_XDECREF
(
n
);
PyMem_Free
(
key
);
return
result
;
}
static
PyObject
*
random_getstate
(
RandomObject
*
self
)
{
PyObject
*
state
;
PyObject
*
element
;
int
i
;
state
=
PyTuple_New
(
N
+
1
);
if
(
state
==
NULL
)
return
NULL
;
for
(
i
=
0
;
i
<
N
;
i
++
)
{
element
=
PyInt_FromLong
((
long
)(
self
->
state
[
i
]));
if
(
element
==
NULL
)
goto
Fail
;
PyTuple_SET_ITEM
(
state
,
i
,
element
);
}
element
=
PyInt_FromLong
((
long
)(
self
->
index
));
if
(
element
==
NULL
)
goto
Fail
;
PyTuple_SET_ITEM
(
state
,
i
,
element
);
return
state
;
Fail:
Py_DECREF
(
state
);
return
NULL
;
}
static
PyObject
*
random_setstate
(
RandomObject
*
self
,
PyObject
*
state
)
{
int
i
;
long
element
;
if
(
!
PyTuple_Check
(
state
))
{
PyErr_SetString
(
PyExc_TypeError
,
"state vector must be a tuple"
);
return
NULL
;
}
if
(
PyTuple_Size
(
state
)
!=
N
+
1
)
{
PyErr_SetString
(
PyExc_ValueError
,
"state vector is the wrong size"
);
return
NULL
;
}
for
(
i
=
0
;
i
<
N
;
i
++
)
{
element
=
PyInt_AsLong
(
PyTuple_GET_ITEM
(
state
,
i
));
if
(
element
==
-
1
&&
PyErr_Occurred
())
return
NULL
;
self
->
state
[
i
]
=
(
unsigned
long
)
element
;
}
element
=
PyInt_AsLong
(
PyTuple_GET_ITEM
(
state
,
i
));
if
(
element
==
-
1
&&
PyErr_Occurred
())
return
NULL
;
self
->
index
=
(
int
)
element
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
/*
Jumpahead should be a fast way advance the generator n-steps ahead, but
lacking a formula for that, the next best is to use n and the existing
state to create a new state far away from the original.
The generator uses constant spaced additive feedback, so shuffling the
state elements ought to produce a state which would not be encountered
(in the near term) by calls to random(). Shuffling is normally
implemented by swapping the ith element with another element ranging
from 0 to i inclusive. That allows the element to have the possibility
of not being moved. Since the goal is to produce a new, different
state, the swap element is ranged from 0 to i-1 inclusive. This assures
that each element gets moved at least once.
To make sure that consecutive calls to jumpahead(n) produce different
states (even in the rare case of involutory shuffles), i+1 is added to
each element at position i. Successive calls are then guaranteed to
have changing (growing) values as well as shuffled positions.
Finally, the self->index value is set to N so that the generator itself
kicks in on the next call to random(). This assures that all results
have been through the generator and do not just reflect alterations to
the underlying state.
*/
static
PyObject
*
random_jumpahead
(
RandomObject
*
self
,
PyObject
*
n
)
{
long
i
,
j
;
PyObject
*
iobj
;
PyObject
*
remobj
;
unsigned
long
*
mt
,
tmp
;
if
(
!
PyInt_Check
(
n
)
&&
!
PyLong_Check
(
n
))
{
PyErr_Format
(
PyExc_TypeError
,
"jumpahead requires an "
"integer, not '%s'"
,
n
->
ob_type
->
tp_name
);
return
NULL
;
}
mt
=
self
->
state
;
for
(
i
=
N
-
1
;
i
>
1
;
i
--
)
{
iobj
=
PyInt_FromLong
(
i
);
if
(
iobj
==
NULL
)
return
NULL
;
remobj
=
PyNumber_Remainder
(
n
,
iobj
);
Py_DECREF
(
iobj
);
if
(
remobj
==
NULL
)
return
NULL
;
j
=
PyInt_AsLong
(
remobj
);
Py_DECREF
(
remobj
);
if
(
j
==
-
1L
&&
PyErr_Occurred
())
return
NULL
;
tmp
=
mt
[
i
];
mt
[
i
]
=
mt
[
j
];
mt
[
j
]
=
tmp
;
}
for
(
i
=
0
;
i
<
N
;
i
++
)
mt
[
i
]
+=
i
+
1
;
self
->
index
=
N
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyObject
*
random_new
(
PyTypeObject
*
type
,
PyObject
*
args
,
PyObject
*
kwds
)
{
RandomObject
*
self
;
PyObject
*
tmp
;
self
=
(
RandomObject
*
)
type
->
tp_alloc
(
type
,
0
);
if
(
self
==
NULL
)
return
NULL
;
tmp
=
random_seed
(
self
,
args
);
if
(
tmp
==
NULL
)
{
Py_DECREF
(
self
);
return
NULL
;
}
Py_DECREF
(
tmp
);
return
(
PyObject
*
)
self
;
}
static
PyMethodDef
random_methods
[]
=
{
{
"random"
,
(
PyCFunction
)
random_random
,
METH_NOARGS
,
PyDoc_STR
(
"random() -> x in the interval [0, 1)."
)},
{
"seed"
,
(
PyCFunction
)
random_seed
,
METH_VARARGS
,
PyDoc_STR
(
"seed([n]) -> None. Defaults to current time."
)},
{
"getstate"
,
(
PyCFunction
)
random_getstate
,
METH_NOARGS
,
PyDoc_STR
(
"getstate() -> tuple containing the current state."
)},
{
"setstate"
,
(
PyCFunction
)
random_setstate
,
METH_O
,
PyDoc_STR
(
"setstate(state) -> None. Restores generator state."
)},
{
"jumpahead"
,
(
PyCFunction
)
random_jumpahead
,
METH_O
,
PyDoc_STR
(
"jumpahead(int) -> None. Create new state from "
"existing state and integer."
)},
{
NULL
,
NULL
}
/* sentinel */
};
PyDoc_STRVAR
(
random_doc
,
"Random() -> create a random number generator with its own internal state."
);
static
PyTypeObject
Random_Type
=
{
PyObject_HEAD_INIT
(
NULL
)
0
,
/*ob_size*/
"_random.Random"
,
/*tp_name*/
sizeof
(
RandomObject
),
/*tp_basicsize*/
0
,
/*tp_itemsize*/
/* methods */
0
,
/*tp_dealloc*/
0
,
/*tp_print*/
0
,
/*tp_getattr*/
0
,
/*tp_setattr*/
0
,
/*tp_compare*/
0
,
/*tp_repr*/
0
,
/*tp_as_number*/
0
,
/*tp_as_sequence*/
0
,
/*tp_as_mapping*/
0
,
/*tp_hash*/
0
,
/*tp_call*/
0
,
/*tp_str*/
PyObject_GenericGetAttr
,
/*tp_getattro*/
0
,
/*tp_setattro*/
0
,
/*tp_as_buffer*/
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
,
/*tp_flags*/
random_doc
,
/*tp_doc*/
0
,
/*tp_traverse*/
0
,
/*tp_clear*/
0
,
/*tp_richcompare*/
0
,
/*tp_weaklistoffset*/
0
,
/*tp_iter*/
0
,
/*tp_iternext*/
random_methods
,
/*tp_methods*/
0
,
/*tp_members*/
0
,
/*tp_getset*/
0
,
/*tp_base*/
0
,
/*tp_dict*/
0
,
/*tp_descr_get*/
0
,
/*tp_descr_set*/
0
,
/*tp_dictoffset*/
0
,
/*tp_init*/
PyType_GenericAlloc
,
/*tp_alloc*/
random_new
,
/*tp_new*/
_PyObject_Del
,
/*tp_free*/
0
,
/*tp_is_gc*/
};
PyDoc_STRVAR
(
module_doc
,
"Module implements the Mersenne Twister random number generator."
);
PyMODINIT_FUNC
init_random
(
void
)
{
PyObject
*
m
;
if
(
PyType_Ready
(
&
Random_Type
)
<
0
)
return
;
m
=
Py_InitModule3
(
"_random"
,
NULL
,
module_doc
);
Py_INCREF
(
&
Random_Type
);
PyModule_AddObject
(
m
,
"Random"
,
(
PyObject
*
)
&
Random_Type
);
}
setup.py
View file @
8ff3d50e
...
...
@@ -316,6 +316,8 @@ class PyBuildExt(build_ext):
libraries
=
math_libs
)
)
exts
.
append
(
Extension
(
'datetime'
,
[
'datetimemodule.c'
],
libraries
=
math_libs
)
)
# random number generator implemented in C
exts
.
append
(
Extension
(
"_random"
,
[
"_randommodule.c"
])
)
# operator.add() and similar goodies
exts
.
append
(
Extension
(
'operator'
,
[
'operator.c'
])
)
# Python C API test module
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment