-
Notifications
You must be signed in to change notification settings - Fork 0
/
mersenne_twister.clj
executable file
·76 lines (60 loc) · 2.12 KB
/
mersenne_twister.clj
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
(ns util.mersenne-twister
(:require [util.tools :as u]))
(def MAX-INT32 0xFFFFFFFF)
(def INT32-BIT 0x80000000)
(def MAX-INT31 0x7FFFFFFF)
(def TEMPER-MASK1 2636928640)
(def TEMPER-MASK2 4022730752)
(def BIT-MASK1 1812433253)
(def BIT-MASK2 2567483615)
;; MT state
(def STATE (atom [(vec (repeat 624 0)) ;; state
0 ;; index
0])) ;; last-random-number generated
(def STATE-LEN 624)
(defn initialize-generator
"Initialize random number generator with provided seed."
[seed]
(reset! STATE
(loop [i 1
MT [(u/& MAX-INT32 seed)]]
(if (>= i STATE-LEN)
[MT 0 0]
(recur (inc i)
(conj MT
(u/& MAX-INT32
(u/! (* BIT-MASK1 (peek MT))
(+ i (u/>> (peek MT)
30)))))))))
nil)
(defn twist
"Generate next mersenne state"
[MT]
(mapv (fn [i]
(let [y (+ (u/& (MT i) INT32-BIT)
(u/& (MT (rem (inc i) STATE-LEN))
MAX-INT31))]
(cond-> (u/! (MT (rem (+ i 397) STATE-LEN))
(u/>> y 1))
(u/divides-not? y 2) (u/! BIT-MASK2))))
(range STATE-LEN)))
(defn extract-number
"Output random number"
[]
(get (swap! STATE (fn [[MT index _]]
(let [MT (if (zero? index) (twist MT) MT)]
[MT
(rem (inc index) STATE-LEN)
(-> (MT index)
(#(u/! % (u/>> % 11)))
(#(u/! % (u/& (u/<< % 7) TEMPER-MASK1)))
(#(u/! % (u/& (u/<< % 11) TEMPER-MASK2)))
(#(u/! % (u/>> % 18))))]
)))
2)) ;; Update state and output the last generated random number
(defn rand-num
"Return a pseudo-random number in the given range a <= x < b"
([b] (mod (extract-number) b))
([a b] (+ a (mod (extract-number) (- b a)))))
;; Initialize PRNG
;; (initialize-generator 0)