You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: chapters/math/generating-predictable-random-numbers.textile
+64-3Lines changed: 64 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,13 +10,74 @@ You need to generate a random number in a certain range, but you also need to be
10
10
11
11
h2. Solution
12
12
13
-
Write your own random number generator. There are a LOT of ways to do this. Here's a simple one.
13
+
Write your own random number generator. There are a LOT of ways to do this. Here's a simple one. _This generator is +ABSOLUTELY NOT+ acceptable for cryptographic purposes!_
14
14
15
15
{% highlight coffeescript %}
16
-
# TODO: write simple linear PRNG
16
+
class Rand
17
+
# if created without a seed, uses current time as seed
18
+
constructor: (@seed) ->
19
+
# Knuth and Lewis' improvements to Park and Miller's LCPRNG
20
+
@multiplier = 1664525
21
+
@modulo = 4294967296 # 2**32-1;
22
+
@offset = 1013904223
23
+
unless @seed? && 0 <= seed < @modulo
24
+
@seed = (new Date().valueOf() * new Date().getMilliseconds()) % @modulo
25
+
26
+
# sets new seed value
27
+
seed: (seed) ->
28
+
@seed = seed
29
+
30
+
# return a random integer 0 <= n < @modulo
31
+
randn:
32
+
# new_seed = (a * seed + c) % m
33
+
@seed = (@multiplier*@seed + @offset) % @modulo
34
+
35
+
# return a random float 0 <= f < 1.0
36
+
rand: ->
37
+
this.randn() / @modulo
38
+
39
+
# return a random int 0 <= f < n
40
+
randi: (n) ->
41
+
Math.floor(this.rand() * n)
42
+
43
+
r = new Rand 0
44
+
r.randn().toString(16)
45
+
# => "3c6ef35f"
46
+
47
+
r.randn().toString(16)
48
+
# => "47502932"
49
+
50
+
r.randn().toString(16)
51
+
# => "d1ccf6e9"
52
+
53
+
r.randn().toString(16)
54
+
# => "aaf95334"
55
+
56
+
r.randn().toString(16)
57
+
# => "6252e503"
58
+
59
+
r.randn().toString(16)
60
+
# => "9f2ec686"
17
61
{% endhighlight %}
18
62
19
63
h2. Discussion
20
64
21
-
JavaScript and CoffeeScript do not provide a seedable random number generator. Writing your own will be an exercise in trading off the amount of randomness with the simplicity of the generator. A full discussion of randomness is beyond the scope of this cookbook; for further reading consult Donald Knuths _The Art of Computer Programming_, Volume II.
65
+
JavaScript and CoffeeScript do not provide a seedable random number generator. Writing your own will be an exercise in trading off the amount of randomness with the simplicity of the generator. A full discussion of randomness is beyond the scope of this cookbook; for further reading consult Donald Knuth's _The Art of Computer Programming_, Volume II, Chapter 3, "Random Numbers", and _Numerical Recipes in C_, 2nd Edition, Chapter 7, "Random Numbers".
66
+
67
+
A brief explanation of this random number generator is in order, however. It is a Linear Congruential Pseudorandom Number Generator. LCPRNG's operate on the mathematical formula <tt>I<sub>j+1</sub> = (aI<sub>j</sub>+c) % m</tt>, where a is the multiplier, c is the addition offset, and m is the modulus.
68
+
Each time a random number is requested, a very large multiplication and addition are performed—"very large" relative to the key space—and the resulting number is modulused back down into the keyspace.
69
+
70
+
This generator has a period of 2<sup>32</sup>. It is absolutely unacceptable for cryptographic purposes, but for most simple randomness requirements it is quite adequate. randn() will traverse the entire keyspace before repeating itself, and the next number is determined by the previous one.
71
+
72
+
If you want to tinker with this generator, you are _strongly_ encouraged to read Chapter 3 of Knuth's _The Art of Computer Programming_. Random number generation is VERY easy to screw up, and Knuth explains how to tell a good RNG from a bad one.
73
+
74
+
Avoid the temptation to modulus the output of this generator. If you need an integer range, use division. Linear Congruential generators are very nonrandom in their lower bits. This one in particular always generates an odd number from an even seed, and vice versa. So if you need a random 0 or 1, do NOT use
75
+
76
+
{% highlight coffeescript %}
77
+
# NOT random! Do not do this!
78
+
r.randn() % 2
79
+
{% endhighlight %}
80
+
81
+
because you will most definitely not get random digits. Use <tt>r.randi(2)</tt> instead.
0 commit comments