Class: SimpleRandom

Inherits:
Object
  • Object
show all
Defined in:
lib/simple-random/simple_random.rb

Direct Known Subclasses

MultiThreadedSimpleRandom

Defined Under Namespace

Classes: InvalidSeedArgument

Constant Summary collapse

I_32_BIT =
4294967296
F_32_BIT =
4294967296.0
DEFAULT_SEEDS =
[521288629, 362436069]

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ SimpleRandom

Returns a new instance of SimpleRandom.



8
9
10
11
12
13
14
# File 'lib/simple-random/simple_random.rb', line 8

def initialize(*args)
  if args.empty?
    set_seed(*DEFAULT_SEEDS)
  else
    set_seed(*args)
  end
end

Instance Method Details

#beta(a, b) ⇒ Object



102
103
104
105
106
107
# File 'lib/simple-random/simple_random.rb', line 102

def beta(a, b)
  fail ArgumentError, "Parameters must be strictly positive" unless a > 0 && b > 0
  u = gamma(a, 1)
  v = gamma(b, 1)
  u / (u + v)
end

#cauchy(median, scale) ⇒ Object



115
116
117
118
119
# File 'lib/simple-random/simple_random.rb', line 115

def cauchy(median, scale)
  fail ArgumentError, 'Scale must be positive' unless scale > 0

  median + scale * Math.tan(Math::PI * (uniform - 0.5))
end

#chi_square(degrees_of_freedom) ⇒ Object



94
95
96
# File 'lib/simple-random/simple_random.rb', line 94

def chi_square(degrees_of_freedom)
  gamma(0.5 * degrees_of_freedom, 2.0)
end

#dirichlet(*parameters) ⇒ Object



139
140
141
142
143
# File 'lib/simple-random/simple_random.rb', line 139

def dirichlet(*parameters)
  sample = parameters.map { |a| gamma(a, 1) }
  sum = sample.inject(0.0) { |sum, g| sum + g }
  sample.map { |g| g / sum }
end

#exponential(mean = 1) ⇒ Object

Get exponential random sample with specified mean



47
48
49
50
51
# File 'lib/simple-random/simple_random.rb', line 47

def exponential(mean = 1)
  fail ArgumentError, "Mean must be strictly positive" unless mean > 0

  -1.0 * mean * Math.log(uniform)
end

#gamma(shape, scale) ⇒ Object

Implementation based on “A Simple Method for Generating Gamma Variables” by George Marsaglia and Wai Wan Tsang. ACM Transactions on Mathematical Software Vol 26, No 3, September 2000, pages 363-372.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/simple-random/simple_random.rb', line 71

def gamma(shape, scale)
  fail ArgumentError, 'Shape must be strictly positive' unless shape > 0
  return scale * gamma(shape + 1.0, 1.0) * uniform ** -shape if shape < 1

  d = shape - 1 / 3.0
  c = (9 * d) ** -0.5

  begin
    z = normal

    condition1 = z > (-1.0 / c)
    condition2 = false

    if condition1
      u = uniform
      v = (1 + c * z) ** 3
      condition2 = Math.log(u) < (0.5 * (z ** 2) + d * (1.0 - v + Math.log(v)))
    end
  end while !condition2

  scale * d * v
end

#inverse_gamma(shape, scale) ⇒ Object



98
99
100
# File 'lib/simple-random/simple_random.rb', line 98

def inverse_gamma(shape, scale)
  1.0 / gamma(shape, 1.0 / scale)
end

#laplace(mean, scale) ⇒ Object



127
128
129
130
131
132
133
# File 'lib/simple-random/simple_random.rb', line 127

def laplace(mean, scale)
  u_1 = uniform(-0.5, 0.5)
  u_2 = uniform

  sign = u_1 / u_1.abs
  mean + sign * scale * Math.log(1 - u_2)
end

#log_normal(mu, sigma) ⇒ Object



135
136
137
# File 'lib/simple-random/simple_random.rb', line 135

def log_normal(mu, sigma)
  Math.exp(normal(mu, sigma))
end

#normal(mean = 0.0, standard_deviation = 1.0) ⇒ Object

Sample normal distribution with given mean and standard deviation



40
41
42
43
44
# File 'lib/simple-random/simple_random.rb', line 40

def normal(mean = 0.0, standard_deviation = 1.0)
  fail ArgumentError, 'Standard deviation must be strictly positive' unless standard_deviation > 0

  mean + standard_deviation * ((-2.0 * Math.log(uniform)) ** 0.5) * Math.sin(2.0 * Math::PI * uniform)
end

#seedsObject



28
29
30
# File 'lib/simple-random/simple_random.rb', line 28

def seeds
  [@m_w, @m_z]
end

#seeds=(value) ⇒ Object



24
25
26
# File 'lib/simple-random/simple_random.rb', line 24

def seeds=(value)
  set_seed(*[value].flatten.compact)
end

#set_seed(*args) ⇒ Object



16
17
18
19
20
21
22
# File 'lib/simple-random/simple_random.rb', line 16

def set_seed(*args)
  validate_seeds!(*args)

  @m_w, @m_z = determine_seeds(*args)

  ensure_32bit_seeds!
end

#student_t(degrees_of_freedom) ⇒ Object



121
122
123
124
125
# File 'lib/simple-random/simple_random.rb', line 121

def student_t(degrees_of_freedom)
  fail ArgumentError, 'Degrees of freedom must be strictly positive' unless degrees_of_freedom > 0

  normal / ((chi_square(degrees_of_freedom) / degrees_of_freedom) ** 0.5)
end

#triangular(lower, mode, upper) ⇒ Object

Get triangular random sample with specified lower limit, mode, upper limit



54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/simple-random/simple_random.rb', line 54

def triangular(lower, mode, upper)
  fail ArgumentError, 'Upper bound must be greater than lower bound.' unless lower < upper
  fail ArgumentError, 'Mode must lie between the upper and lower limits' if mode > upper || mode < lower

  r = (upper - lower).to_f
  u = uniform

  if u < ((mode - lower) / r)
    lower + Math.sqrt(u * r * (mode - lower))
  else
    upper - Math.sqrt((1.0 - u) * r * (upper - mode))
  end
end

#uniform(lower = 0, upper = 1) ⇒ Object

Produce a uniform random sample from the open interval (lower, upper).



33
34
35
36
37
# File 'lib/simple-random/simple_random.rb', line 33

def uniform(lower = 0, upper = 1)
  fail ArgumentError, 'Upper bound must be greater than lower bound.' unless lower < upper

  ((get_unsigned_int + 1) * (upper - lower) / F_32_BIT) + lower
end

#weibull(shape, scale) ⇒ Object



109
110
111
112
113
# File 'lib/simple-random/simple_random.rb', line 109

def weibull(shape, scale)
  fail ArgumentError, 'Shape and scale must be positive' unless shape > 0 && scale > 0

  scale * ((-Math.log(uniform)) ** (1.0 / shape))
end