goalseek 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 747c7855df771b99e8a14ae0d7dda32bda5216c4
4
- data.tar.gz: 64351ed03ddb34be21d332a3eeaef5f60505e51c
3
+ metadata.gz: ef1c7fc4ff85d9cb3a5033fa6f9b18c856f60bd5
4
+ data.tar.gz: f7e85129e6e10ddc37b0e67c56dec035503922e1
5
5
  SHA512:
6
- metadata.gz: 3a6def3a9b3e69033c550fbbe666de917335c53eab464ed94a0fea65b634cf8af2bb6be4de47294e76da6e2dfd8e4703dd2d95aadb29c671aee383858820e200
7
- data.tar.gz: eca9ff2df3c75ce9b552cd9a0e71e5133ef45fc50d46b94654e5350fed2787a10dab7990f3acc452d3763ea2eb3796f1e62d2f08aa07eda8b676157f37f8a729
6
+ metadata.gz: 68a6ac63ae127ec11ac432ef84f4ee67cf5d99b45c48c70c28effc703d4fdd49f2cfbffe9931201f8fa7e6f5bd44d9d407d9a8cbae5535a985b22b09f5f7dabe
7
+ data.tar.gz: db959a50069beae15e00a9c8cd633b630894f136e9fd8f8ffaad25ad2f5b6e29209fc7c94c55f29a4b69a503638b7c9fa2819ddc6e79856e11df53fe2696c00b
data/README.md CHANGED
@@ -30,7 +30,22 @@ Or install it yourself as:
30
30
 
31
31
  ## Usage
32
32
 
33
- TODO: Write usage instructions here
33
+ This is a simple example of a linear search on a qubic function.
34
+
35
+ Bounds are automatically determined if they are within -10^10 to 10^10. Iterations are limited to 1000 by default.
36
+
37
+ ```ruby
38
+ begin
39
+ goalseeker = GoalSeek::LinearSearch.new(Proc.new() { |x| x ** 3 });
40
+ result = goalseeker.seek(27) # returns 3.0
41
+ rescue GoalSeek::InvalidBoundError
42
+ puts "Function does not cross target value within interval (-10^10, 10^10)"
43
+ rescue GoalSeek::InvalidFunctionError
44
+ puts "Provided block is not callable"
45
+ end
46
+ ```
47
+
48
+ For more examples, please have a look at the specs.
34
49
 
35
50
  ## Development
36
51
 
@@ -1,4 +1,5 @@
1
1
  require 'goalseek/version'
2
+ require 'goalseek/linear_search'
2
3
 
3
4
  module GoalSeek
4
5
  class InvalidBoundError < Exception
@@ -6,83 +7,4 @@ module GoalSeek
6
7
 
7
8
  class InvalidFunctionError < Exception
8
9
  end
9
-
10
- class GoalSeek
11
-
12
- # block is an f(x) function
13
- def initialize(f)
14
- @f = f
15
- end
16
-
17
- def seek(goal, options = {})
18
- @goal = goal.to_f
19
-
20
- options = {
21
- tolerance: 0,
22
- max_iterations: 1000
23
- }.merge(options)
24
-
25
- min_max = get_min_max(options[:lower_bound], options[:upper_bound])
26
- lower, upper = min_max[0], min_max[1]
27
-
28
- iterate(lower, upper, options[:tolerance], options[:max_iterations])
29
- end
30
-
31
- def iterate(lower, upper, tolerance, max_iterations, iterations = 1)
32
- new_bound = (lower + upper) / 2
33
-
34
- begin
35
- new_value = @f.call(new_bound)
36
- rescue NoMethodError
37
- raise InvalidFunctionError
38
- end
39
-
40
- if iterations == max_iterations || new_value === @goal
41
- return new_bound
42
- elsif new_value < @goal
43
- return iterate(new_bound, upper, tolerance, max_iterations, iterations + 1)
44
- elsif new_value > @goal
45
- return iterate(lower, new_bound, tolerance, max_iterations, iterations + 1)
46
- end
47
- end
48
-
49
- def get_min_max(a, b)
50
- x = nil, y = nil
51
-
52
- # Use given bounds if present
53
- if a && b
54
- x = a.to_f
55
- y = b.to_f
56
-
57
- if check_bounds(x, y)
58
- [x, y]
59
- elsif check_bounds(y, x)
60
- [y, x]
61
- else
62
- raise InvalidBoundError
63
- end
64
- end
65
-
66
- # Try exponential bound expansion from 0 to 10^10
67
- (0..10).each do |i|
68
- if check_bounds(0.0, 10.0 ** i)
69
- return [0.0, 10.0 ** i]
70
- end
71
- end
72
-
73
- # Try exponential bound expansion from -x to x
74
- (0..10).each do |i|
75
- if check_bounds(-10.0 ** i, 10.0 ** i)
76
- return [-10.0 ** i, 10.0 ** i]
77
- end
78
- end
79
-
80
- # Fall back to raising if no working bound could be found
81
- raise InvalidBoundError
82
- end
83
-
84
- def check_bounds(x, y)
85
- (@f.call(x) < @goal) && (@f.call(y) > @goal) || (@f.call(y) < @goal) && (@f.call(x) > @goal)
86
- end
87
- end
88
10
  end
@@ -0,0 +1,77 @@
1
+ module GoalSeek
2
+ class LinearSearch
3
+ # block is an f(x) function
4
+ def initialize(f)
5
+ @f = f
6
+ end
7
+
8
+ def seek(goal, options = {})
9
+ @goal = goal.to_f
10
+
11
+ options = {
12
+ tolerance: 0,
13
+ max_iterations: 1000
14
+ }.merge(options)
15
+
16
+ min_max = get_min_max(options[:lower_bound], options[:upper_bound])
17
+ lower, upper = min_max[0], min_max[1]
18
+
19
+ iterate(lower, upper, options[:tolerance], options[:max_iterations])
20
+ end
21
+
22
+ def iterate(lower, upper, tolerance, max_iterations, iterations = 1)
23
+ new_bound = (lower + upper) / 2
24
+
25
+ begin
26
+ new_value = @f.call(new_bound)
27
+ rescue NoMethodError
28
+ raise InvalidFunctionError
29
+ end
30
+
31
+ if iterations == max_iterations || new_value === @goal
32
+ return new_bound
33
+ elsif new_value < @goal
34
+ return iterate(new_bound, upper, tolerance, max_iterations, iterations + 1)
35
+ elsif new_value > @goal
36
+ return iterate(lower, new_bound, tolerance, max_iterations, iterations + 1)
37
+ end
38
+ end
39
+
40
+ def get_min_max(a, b)
41
+ # Use given bounds if present
42
+ if a && b
43
+ x = a.to_f
44
+ y = b.to_f
45
+
46
+ if check_bounds(x, y)
47
+ [x, y]
48
+ elsif check_bounds(y, x)
49
+ [y, x]
50
+ else
51
+ raise InvalidBoundError
52
+ end
53
+ end
54
+
55
+ # Try exponential bound expansion from 0 to 10^10
56
+ (0..10).each do |i|
57
+ if check_bounds(0.0, 10.0 ** i)
58
+ return [0.0, 10.0 ** i]
59
+ end
60
+ end
61
+
62
+ # Try exponential bound expansion from -x to x
63
+ (0..10).each do |i|
64
+ if check_bounds(-10.0 ** i, 10.0 ** i)
65
+ return [-10.0 ** i, 10.0 ** i]
66
+ end
67
+ end
68
+
69
+ # Fall back to raising if no working bound could be found
70
+ raise InvalidBoundError
71
+ end
72
+
73
+ def check_bounds(x, y)
74
+ (@f.call(x) < @goal) && (@f.call(y) > @goal) || (@f.call(y) < @goal) && (@f.call(x) > @goal)
75
+ end
76
+ end
77
+ end
@@ -1,3 +1,3 @@
1
1
  module GoalSeek
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: goalseek
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pascal Ehlert
@@ -72,6 +72,7 @@ files:
72
72
  - bin/setup
73
73
  - goalseek.gemspec
74
74
  - lib/goalseek.rb
75
+ - lib/goalseek/linear_search.rb
75
76
  - lib/goalseek/version.rb
76
77
  homepage: https://github.com/pehlert/goalseek
77
78
  licenses: