class RV::Triangle

Triangular random variate generator with specified min, mode, and max.

Arguments
  • rng -> the (Enumerable) source of U(0, 1)'s (default: U_GENERATOR)

  • min -> the lower bound for the range.

  • max -> the upper bound for the range.

  • mode -> the highest likelihood value (minmodemax).

  • mean -> the expected value of the distribution.

Attributes

max[R]
mean[R]
min[R]
mode[R]
range[R]

Public Class Methods

new(rng: U_GENERATOR, **args) click to toggle source
# File lib/random_variates.rb, line 72
def initialize(rng: U_GENERATOR, **args)
  param_names = %i[mean min max mode]
  unless args.size > 2 && args.keys.all? { |k| param_names.include? k }
    raise "invalid args - can only be #{param_names.join ', '}, or rng."
  end

  param_names.each { |k| args[k] ||= nil } if args.size < param_names.size

  nil_args = args.select { |_, v| v.nil? }.keys
  nil_args_count = nil_args.count
  raise 'too many nil args' if nil_args_count > 1

  args.transform_values! &:to_f

  if nil_args_count == 0
    if args[:mean] != (args[:min] + args[:max] + args[:mode]) / 3.0
      raise 'inconsistent args'
    end
  else
    key = nil_args.shift
    case key
    when :mean
      args[key] = (args[:min] + args[:max] + args[:mode]) / 3.0
    else
      others = param_names - [key, :mean]
      args[key] = 3 * args[:mean] - args.values_at(*others).sum
    end
  end

  param_names.each { |parm| instance_variable_set("@#{parm}", args[parm]) }

  @rng = rng
  @range = @max - @min
  raise 'Min must be less than Max.' if @range <= 0
  unless (@min..@max).include? @mode
    raise 'Mode must be between Min and Max.'
  end
  @crossover_p = (@mode - @min).to_f / @range
end

Public Instance Methods

next() click to toggle source
# File lib/random_variates.rb, line 112
def next
  u = @rng.next
  u < @crossover_p ?
    @min + Math.sqrt(@range * (@mode - @min) * u) :
    @max - Math.sqrt(@range * (@max - @mode) * (1.0 - u))
end