class GoalSeek::LinearSearch
Public Class Methods
new(f)
click to toggle source
block is an f(x) function
# File lib/goalseek/linear_search.rb, line 4 def initialize(f) @f = f end
Public Instance Methods
check_bounds(x, y)
click to toggle source
# File lib/goalseek/linear_search.rb, line 73 def check_bounds(x, y) (@f.call(x) < @goal) && (@f.call(y) > @goal) || (@f.call(y) < @goal) && (@f.call(x) > @goal) end
get_min_max(a, b)
click to toggle source
# File lib/goalseek/linear_search.rb, line 40 def get_min_max(a, b) # Use given bounds if present if a && b x = a.to_f y = b.to_f if check_bounds(x, y) [x, y] elsif check_bounds(y, x) [y, x] else raise InvalidBoundError end end # Try exponential bound expansion from 0 to 10^10 (0..10).each do |i| if check_bounds(0.0, 10.0 ** i) return [0.0, 10.0 ** i] end end # Try exponential bound expansion from -x to x (0..10).each do |i| if check_bounds(-10.0 ** i, 10.0 ** i) return [-10.0 ** i, 10.0 ** i] end end # Fall back to raising if no working bound could be found raise InvalidBoundError end
iterate(lower, upper, tolerance, max_iterations, iterations = 1)
click to toggle source
# File lib/goalseek/linear_search.rb, line 22 def iterate(lower, upper, tolerance, max_iterations, iterations = 1) new_bound = (lower + upper) / 2 begin new_value = @f.call(new_bound) rescue NoMethodError raise InvalidFunctionError end if iterations == max_iterations || new_value === @goal return new_bound elsif new_value < @goal return iterate(new_bound, upper, tolerance, max_iterations, iterations + 1) elsif new_value > @goal return iterate(lower, new_bound, tolerance, max_iterations, iterations + 1) end end
seek(goal, options = {})
click to toggle source
# File lib/goalseek/linear_search.rb, line 8 def seek(goal, options = {}) @goal = goal.to_f options = { tolerance: 0, max_iterations: 1000 }.merge(options) min_max = get_min_max(options[:lower_bound], options[:upper_bound]) lower, upper = min_max[0], min_max[1] iterate(lower, upper, options[:tolerance], options[:max_iterations]) end