forked from bmurray7/mersenne-twister-examples
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathperl-mersenne-twister.pl
236 lines (181 loc) · 6.97 KB
/
perl-mersenne-twister.pl
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# This software is Copyright (c) 2008 by James Freeman.
#
# This is free software, licensed under:
#
# The Artistic License 2.0
package Math::Random::MT::Perl;
use strict;
use warnings;
use vars qw($VERSION);
$VERSION = 1.05;
my $N = 624;
my $M = 397;
my $UP_MASK = 0x80000000;
my $LOW_MASK = 0x7fffffff;
my $gen = undef;
sub new {
my ($class, @seeds) = @_;
my $self = {};
bless $self, $class;
@seeds > 1 ? $self->_mt_setup_array(@seeds) :
$self->_mt_init_seed($seeds[0]||time);
return $self;
}
sub rand {
my ($self, $range) = @_;
if (ref $self) {
return ($range || 1) * $self->_mt_genrand();
}
else {
$range = $self;
Math::Random::MT::Perl::srand() unless defined $gen;
return ($range || 1) * $gen->_mt_genrand();
}
}
sub srand { $gen = Math::Random::MT::Perl->new(shift||time) }
# Note that we need to use integer some of the time to force integer overflow
# rollover ie 2**32+1 => 0. Unfortunately we really want uint but integer
# casts to signed ints, thus we can't do everything within an integer block,
# specifically the bitshift xor functions below. The & 0xffffffff is required
# to constrain the integer to 32 bits on 64 bit systems.
sub _mt_init_seed {
my ($self, $seed) = @_;
my @mt;
$mt[0] = $seed & 0xffffffff;
for ( my $i = 1; $i < $N; $i++ ) {
my $xor = $mt[$i-1]^($mt[$i-1]>>30);
{ use integer; $mt[$i] = (1812433253 * $xor + $i) & 0xffffffff }
}
$self->{mt} = \@mt;
$self->{mti} = $N;
}
sub _mt_setup_array {
my ($self, @seeds) = @_;
@seeds = map{ $_ & 0xffffffff }@seeds; # limit seeds to 32 bits
$self->_mt_init_seed( 19650218 );
my @mt = @{$self->{mt}};
my $i = 1;
my $j = 0;
my $n = @seeds;
my $k = $N > $n ? $N : $n;
my ($uint32, $xor);
for (; $k; $k--) {
$xor = $mt[$i-1] ^ ($mt[$i-1] >> 30);
{ use integer; $uint32 = ($xor * 1664525) & 0xffffffff }
$mt[$i] = ($mt[$i] ^ $uint32);
{ use integer; $mt[$i] = ($mt[$i] + $seeds[$j] + $j) & 0xffffffff }
$i++; $j++;
if ($i>=$N) { $mt[0] = $mt[$N-1]; $i=1; }
if ($j>=$n) { $j=0; }
}
for ($k=$N-1; $k; $k--) {
$xor = $mt[$i-1] ^ ($mt[$i-1] >> 30);
{ use integer; $uint32 = ($xor * 1566083941) & 0xffffffff }
$mt[$i] = ($mt[$i] ^ $uint32) - $i;
$i++;
if ($i>=$N) { $mt[0] = $mt[$N-1]; $i=1; }
}
$mt[0] = 0x80000000;
$self->{mt} = \@mt;
}
sub _mt_genrand {
my ($self) = @_;
my ($kk, $y);
my @mag01 = (0x0, 0x9908b0df);
if ($self->{mti} >= $N) {
for ($kk = 0; $kk < $N-$M; $kk++) {
$y = ($self->{mt}->[$kk] & $UP_MASK) | ($self->{mt}->[$kk+1] & $LOW_MASK);
$self->{mt}->[$kk] = $self->{mt}->[$kk+$M] ^ ($y >> 1) ^ $mag01[$y & 1];
}
for (; $kk < $N-1; $kk++) {
$y = ($self->{mt}->[$kk] & $UP_MASK) | ($self->{mt}->[$kk+1] & $LOW_MASK);
$self->{mt}->[$kk] = $self->{mt}->[$kk+($M-$N)] ^ ($y >> 1) ^ $mag01[$y & 1];
}
$y = ($self->{mt}->[$N-1] & $UP_MASK) | ($self->{mt}->[0] & $LOW_MASK);
$self->{mt}->[$N-1] = $self->{mt}->[$M-1] ^ ($y >> 1) ^ $mag01[$y & 1];
$self->{mti} = 0;
}
$y = $self->{mt}->[$self->{mti}++];
$y ^= $y >> 11;
$y ^= ($y << 7) & 0x9d2c5680;
$y ^= ($y << 15) & 0xefc60000;
$y ^= $y >> 18;
return $y*(1.0/4294967296.0);
}
sub import {
no strict 'refs';
my $pkg = caller;
for my $sym (@_) {
*{"${pkg}::$sym"} = \&$sym if $sym eq "srand" or $sym eq "rand";
}
}
1;
__END__
=pod
=for stopwords Abhijit Makoto Menon-Sen Mersenne Nishimura Takuji almut characterised crypto perlmonks pseudorandom gmail
=head1 NAME
Math::Random::MT::Perl - Pure Perl Mersenne Twister Random Number Generator
=head1 SYNOPSIS
use Math::Random::MT::Perl;
$gen = Math::Random::MT->new($seed); # OR...
$gen = Math::Random::MT->new(@seed);
print $gen->rand(42); # random float 0.0 .. 41.99999999 inclusive
$dice = int(1+$gen->rand(6)); # random int between 1 and 6
print $gen->rand() < 0.5 ? "heads" : "tails"
OR
use Math::Random::MT qw(srand rand);
# now srand and rand behave as usual, except with 32 bit precsision not ~15
=head1 DESCRIPTION
Pure Perl implementation of the Mersenne Twister algorithm as implemented in
C/XS in Math::Random::MT. The output is identical to the C/XS version. The
Mersenne Twister is a 32 bit pseudorandom number generator developed by
Makoto Matsumoto and Takuji Nishimura. The algorithm is characterised by
a very uniform distribution but is not cryptographically secure. What this
means in real terms is that it is fine for modeling but no good for crypto.
Note: Internally unsigned 32 bit integers are used. The range of possible
values for such integers is 0..4294967295 (0..2**32-1). The generator
takes a random integer from within this range and multiplies it by
(1.0/4294967296.0). As a result the range of possible return values is
0 .. 0.999999999767169. This number is then multiplied by the argument passed
to rand (default=1). In other words the maximum return value from rand will
always be slightly less than the argument - it will never equal that argument.
Only the first 10 digits of the returned float are mathematically significant.
This module implements the same two interfaces found in Math::Random::MT,
as described in the synopsis above. It defines the following functions.
=head2 Functions
=over
=item new($seed)
Creates a new generator seeded with an unsigned 32-bit integer.
=item new(@seed)
Creates a new generator seeded with an array of unsigned 32-bit integers.
=item rand($num)
Can be called via the OO in interface or exported. Behaves exactly like
Perl's builtin rand(), returning a number uniformly distributed in [0, $num)
($num defaults to 1) except the underlying complexity is 32 bits rather than
some small fraction of this.
=item srand($seed)
Behaves just like Perl's builtin srand(). If you use this interface, it
is strongly recommended that you call I<srand()> explicitly, rather than
relying on I<rand()> to call it the first time it is used. Has no effect if
called via OO interface - pass the seed(s) to new.
=back
=head2 Export
Nothing by default. rand() and srand() on demand.
=head1 SPEED
Runs around 1/3 as fast as the C code of Math::Random::MT, however that still
means a random number generation speed of 100,000/sec on modest hardware.
=head1 SEE ALSO
Math::Random::MT
http://www.math.keio.ac.jp/~matumoto/emt.html
=head1 AUTHOR
(c) Dr James Freeman 2000-08 <airmedical [at] gmail [dot] com>
All rights reserved.
=head2 Credits
almut from perlmonks for 64 bit debug and fix.
Abhijit Menon-Sen, Philip Newton and Sean M. Burke who contributed to
Math::Random::MT as this module is simply a translation.
=head1 LICENSE
This package is free software and is provided "as is" without express or
implied warranty. It may be used, redistributed and/or modified under the
terms of the Artistic License 2.0. A copy is include in this distribution.
=cut