compsci 0.0.3.1 → 0.1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +63 -14
- data/VERSION +1 -1
- data/compsci.gemspec +5 -22
- data/examples/binary_tree.rb +11 -6
- data/examples/complete_tree.rb +47 -0
- data/examples/heap.rb +19 -7
- data/examples/tree.rb +41 -0
- data/lib/compsci.rb +1 -2
- data/lib/compsci/complete_tree.rb +109 -0
- data/lib/compsci/fibonacci.rb +31 -30
- data/lib/compsci/fit.rb +135 -135
- data/lib/compsci/heap.rb +14 -108
- data/lib/compsci/names.rb +132 -0
- data/lib/compsci/node.rb +67 -0
- data/lib/compsci/timer.rb +30 -29
- data/lib/compsci/tree.rb +48 -138
- data/test/bench/tree.rb +2 -2
- data/test/complete_tree.rb +131 -0
- data/test/heap.rb +121 -34
- data/test/names.rb +96 -0
- data/test/node.rb +89 -0
- data/test/tree.rb +49 -237
- metadata +24 -2
data/lib/compsci/fit.rb
CHANGED
@@ -1,137 +1,137 @@
|
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
1
|
+
module CompSci
|
2
|
+
module Fit
|
3
|
+
#
|
4
|
+
# functions below originally from https://github.com/seattlrb/minitest
|
5
|
+
#
|
6
|
+
|
7
|
+
##
|
8
|
+
# Enumerates over +enum+ mapping +block+ if given, returning the
|
9
|
+
# sum of the result. Eg:
|
10
|
+
#
|
11
|
+
# sigma([1, 2, 3]) # => 1 + 2 + 3 => 7
|
12
|
+
# sigma([1, 2, 3]) { |n| n ** 2 } # => 1 + 4 + 9 => 14
|
13
|
+
|
14
|
+
def self.sigma enum, &block
|
15
|
+
enum = enum.map(&block) if block
|
16
|
+
enum.inject { |sum, n| sum + n }
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# Takes an array of x/y pairs and calculates the general R^2 value to
|
21
|
+
# measure fit against a predictive function, which is the block supplied
|
22
|
+
# to error:
|
23
|
+
#
|
24
|
+
# e.g. error(xys) { |x| 5 + 2 * x }
|
25
|
+
#
|
26
|
+
# See: http://en.wikipedia.org/wiki/Coefficient_of_determination
|
27
|
+
#
|
28
|
+
|
29
|
+
def self.error xys, &blk
|
30
|
+
y_bar = sigma(xys) { |_, y| y } / xys.size.to_f
|
31
|
+
ss_tot = sigma(xys) { |_, y| (y - y_bar) ** 2 }
|
32
|
+
ss_res = sigma(xys) { |x, y| (yield(x) - y) ** 2 }
|
33
|
+
|
34
|
+
1 - (ss_res / ss_tot)
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Fits the functional form: a (+ 0x)
|
39
|
+
#
|
40
|
+
# Takes x and y values and returns [a, variance]
|
41
|
+
#
|
42
|
+
|
43
|
+
def self.constant xs, ys
|
44
|
+
# written by Rick
|
45
|
+
y_bar = sigma(ys) / ys.size.to_f
|
46
|
+
variance = sigma(ys) { |y| (y - y_bar) ** 2 }
|
47
|
+
[y_bar, variance]
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# To fit a functional form: y = a + b*ln(x).
|
52
|
+
#
|
53
|
+
# Takes x and y values and returns [a, b, r^2].
|
54
|
+
#
|
55
|
+
# See: http://mathworld.wolfram.com/LeastSquaresFittingLogarithmic.html
|
56
|
+
|
57
|
+
def self.logarithmic xs, ys
|
58
|
+
n = xs.size
|
59
|
+
xys = xs.zip(ys)
|
60
|
+
slnx2 = sigma(xys) { |x, _| Math.log(x) ** 2 }
|
61
|
+
slnx = sigma(xys) { |x, _| Math.log(x) }
|
62
|
+
sylnx = sigma(xys) { |x, y| y * Math.log(x) }
|
63
|
+
sy = sigma(xys) { |_, y| y }
|
64
|
+
|
65
|
+
c = n * slnx2 - slnx ** 2
|
66
|
+
b = ( n * sylnx - sy * slnx ) / c
|
67
|
+
a = (sy - b * slnx) / n
|
68
|
+
|
69
|
+
return a, b, self.error(xys) { |x| a + b * Math.log(x) }
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Fits the functional form: a + bx.
|
74
|
+
#
|
75
|
+
# Takes x and y values and returns [a, b, r^2].
|
76
|
+
#
|
77
|
+
# See: http://mathworld.wolfram.com/LeastSquaresFitting.html
|
78
|
+
|
79
|
+
def self.linear xs, ys
|
80
|
+
n = xs.size
|
81
|
+
xys = xs.zip(ys)
|
82
|
+
sx = sigma xs
|
83
|
+
sy = sigma ys
|
84
|
+
sx2 = sigma(xs) { |x| x ** 2 }
|
85
|
+
sxy = sigma(xys) { |x, y| x * y }
|
86
|
+
|
87
|
+
c = n * sx2 - sx**2
|
88
|
+
a = (sy * sx2 - sx * sxy) / c
|
89
|
+
b = ( n * sxy - sx * sy ) / c
|
90
|
+
|
91
|
+
return a, b, self.error(xys) { |x| a + b * x }
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# To fit a functional form: y = ae^(bx).
|
96
|
+
#
|
97
|
+
# Takes x and y values and returns [a, b, r^2].
|
98
|
+
#
|
99
|
+
# See: http://mathworld.wolfram.com/LeastSquaresFittingExponential.html
|
100
|
+
|
101
|
+
def self.exponential xs, ys
|
102
|
+
n = xs.size
|
103
|
+
xys = xs.zip(ys)
|
104
|
+
sxlny = sigma(xys) { |x, y| x * Math.log(y) }
|
105
|
+
slny = sigma(xys) { |_, y| Math.log(y) }
|
106
|
+
sx2 = sigma(xys) { |x, _| x * x }
|
107
|
+
sx = sigma xs
|
108
|
+
|
109
|
+
c = n * sx2 - sx ** 2
|
110
|
+
a = (slny * sx2 - sx * sxlny) / c
|
111
|
+
b = ( n * sxlny - sx * slny ) / c
|
112
|
+
|
113
|
+
return Math.exp(a), b, self.error(xys) { |x| Math.exp(a + b * x) }
|
114
|
+
end
|
115
|
+
|
116
|
+
##
|
117
|
+
# To fit a functional form: y = ax^b.
|
118
|
+
#
|
119
|
+
# Takes x and y values and returns [a, b, r^2].
|
120
|
+
#
|
121
|
+
# See: http://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html
|
122
|
+
|
123
|
+
def self.power xs, ys
|
124
|
+
n = xs.size
|
125
|
+
xys = xs.zip(ys)
|
126
|
+
slnxlny = sigma(xys) { |x, y| Math.log(x) * Math.log(y) }
|
127
|
+
slnx = sigma(xs) { |x | Math.log(x) }
|
128
|
+
slny = sigma(ys) { | y| Math.log(y) }
|
129
|
+
slnx2 = sigma(xs) { |x | Math.log(x) ** 2 }
|
130
|
+
|
131
|
+
b = (n * slnxlny - slnx * slny) / (n * slnx2 - slnx ** 2)
|
132
|
+
a = (slny - b * slnx) / n
|
133
|
+
|
134
|
+
return Math.exp(a), b, self.error(xys) { |x| (Math.exp(a) * (x ** b)) }
|
135
|
+
end
|
136
136
|
end
|
137
137
|
end
|
data/lib/compsci/heap.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
require 'compsci/
|
2
|
-
|
3
|
-
include CompSci
|
1
|
+
require 'compsci/complete_tree'
|
4
2
|
|
5
3
|
# A Heap is a partially sorted, complete N-ary tree with the property:
|
6
4
|
# * Every node has a value larger (or smaller) than that of its children
|
@@ -20,7 +18,7 @@ include CompSci
|
|
20
18
|
# swap nodes at each layer of the tree, and there are log(n, base b) layers
|
21
19
|
# to the tree.
|
22
20
|
#
|
23
|
-
class Heap < CompleteNaryTree
|
21
|
+
class CompSci::Heap < CompSci::CompleteNaryTree
|
24
22
|
# * defaults to a MaxHeap, with the largest node at the root
|
25
23
|
# * specify a minheap with minheap: true or cmp_val: -1
|
26
24
|
#
|
@@ -39,8 +37,8 @@ class Heap < CompleteNaryTree
|
|
39
37
|
# * sift_up -- O(log n) on heap size
|
40
38
|
#
|
41
39
|
def push(node)
|
42
|
-
@
|
43
|
-
self.sift_up(@
|
40
|
+
@array.push(node)
|
41
|
+
self.sift_up(@array.size - 1)
|
44
42
|
end
|
45
43
|
|
46
44
|
# * remove from the front of the array
|
@@ -48,9 +46,9 @@ class Heap < CompleteNaryTree
|
|
48
46
|
# * sift_down -- O(log n) on heap size
|
49
47
|
#
|
50
48
|
def pop
|
51
|
-
node = @
|
52
|
-
replacement = @
|
53
|
-
@
|
49
|
+
node = @array.shift
|
50
|
+
replacement = @array.pop
|
51
|
+
@array.unshift replacement if replacement
|
54
52
|
self.sift_down(0)
|
55
53
|
node
|
56
54
|
end
|
@@ -58,7 +56,7 @@ class Heap < CompleteNaryTree
|
|
58
56
|
# * return what pop would return (avoid sifting)
|
59
57
|
#
|
60
58
|
def peek
|
61
|
-
@
|
59
|
+
@array.first
|
62
60
|
end
|
63
61
|
|
64
62
|
# * called recursively
|
@@ -69,7 +67,7 @@ class Heap < CompleteNaryTree
|
|
69
67
|
return self if idx <= 0
|
70
68
|
pidx = self.class.parent_idx(idx, @child_slots)
|
71
69
|
if !self.heapish?(pidx, idx)
|
72
|
-
@
|
70
|
+
@array[idx], @array[pidx] = @array[pidx], @array[idx] # swap
|
73
71
|
self.sift_up(pidx)
|
74
72
|
end
|
75
73
|
self
|
@@ -81,12 +79,12 @@ class Heap < CompleteNaryTree
|
|
81
79
|
# * slower than sift_up because one parent vs multiple children
|
82
80
|
#
|
83
81
|
def sift_down(idx)
|
84
|
-
return self if idx >= @
|
82
|
+
return self if idx >= @array.size
|
85
83
|
cidxs = self.class.children_idx(idx, @child_slots)
|
86
84
|
# promote the heapiest child
|
87
85
|
cidx = self.heapiest(cidxs)
|
88
86
|
if !self.heapish?(idx, cidx)
|
89
|
-
@
|
87
|
+
@array[idx], @array[cidx] = @array[cidx], @array[idx] # swap
|
90
88
|
self.sift_down(cidx)
|
91
89
|
end
|
92
90
|
self
|
@@ -95,7 +93,7 @@ class Heap < CompleteNaryTree
|
|
95
93
|
# are values of parent and child (by index) in accordance with heap property?
|
96
94
|
#
|
97
95
|
def heapish?(pidx, cidx)
|
98
|
-
(@
|
96
|
+
(@array[pidx] <=> @array[cidx]) != (@cmp_val * -1)
|
99
97
|
end
|
100
98
|
|
101
99
|
# return the heapiest idx in cidxs
|
@@ -103,7 +101,7 @@ class Heap < CompleteNaryTree
|
|
103
101
|
def heapiest(cidxs)
|
104
102
|
idx = cidxs.first
|
105
103
|
cidxs.each { |cidx|
|
106
|
-
idx = cidx if cidx < @
|
104
|
+
idx = cidx if cidx < @array.size and self.heapish?(cidx, idx)
|
107
105
|
}
|
108
106
|
idx
|
109
107
|
end
|
@@ -116,99 +114,7 @@ class Heap < CompleteNaryTree
|
|
116
114
|
check_children = []
|
117
115
|
self.class.children_idx(idx, @child_slots).each { |cidx|
|
118
116
|
# cidx is arithmetically produced; the corresponding child may not exist
|
119
|
-
if cidx < @
|
120
|
-
return false unless self.heapish?(idx, cidx)
|
121
|
-
check_children << cidx
|
122
|
-
end
|
123
|
-
}
|
124
|
-
check_children.each { |cidx| return false unless self.heap?(idx: cidx) }
|
125
|
-
true
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
#
|
130
|
-
# LEGACY BELOW untouched for now
|
131
|
-
#
|
132
|
-
|
133
|
-
class BinaryHeap < CompleteBinaryTree
|
134
|
-
# defaults to a MaxHeap, with the largest node at the root
|
135
|
-
# specify a minheap with minheap: true or cmp_val: -1
|
136
|
-
#
|
137
|
-
def initialize(cmp_val: 1, minheap: false)
|
138
|
-
super()
|
139
|
-
cmp_val = -1 if minheap
|
140
|
-
case cmp_val
|
141
|
-
when -1, 1
|
142
|
-
@cmp_val = cmp_val
|
143
|
-
else
|
144
|
-
raise(ArgumentError, "unknown comparison value: #{cmp_val}")
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
# append to the array
|
149
|
-
# sift_up -- O(log n) on heap size
|
150
|
-
def push(node)
|
151
|
-
@store << node
|
152
|
-
self.sift_up(@store.size - 1)
|
153
|
-
end
|
154
|
-
|
155
|
-
# remove from the front of the array
|
156
|
-
# move last node to root
|
157
|
-
# sift_down -- O(log n) on heap size
|
158
|
-
def pop
|
159
|
-
node = @store.shift
|
160
|
-
replacement = @store.pop
|
161
|
-
@store.unshift replacement if replacement
|
162
|
-
self.sift_down(0)
|
163
|
-
node
|
164
|
-
end
|
165
|
-
|
166
|
-
# return what pop would return (avoid sifting)
|
167
|
-
def peek
|
168
|
-
@store.first
|
169
|
-
end
|
170
|
-
|
171
|
-
# called recursively
|
172
|
-
# idx represents the node suspected to violate the heap
|
173
|
-
# intended to be O(log n) on heap size
|
174
|
-
def sift_up(idx)
|
175
|
-
return self if idx <= 0
|
176
|
-
pidx = self.class.parent_idx(idx)
|
177
|
-
if !self.heapish?(pidx, idx)
|
178
|
-
@store[idx], @store[pidx] = @store[pidx], @store[idx] # swap
|
179
|
-
self.sift_up(pidx)
|
180
|
-
end
|
181
|
-
self
|
182
|
-
end
|
183
|
-
|
184
|
-
# called recursively
|
185
|
-
# idx represents the node suspected to violate the heap
|
186
|
-
# intended to be O(log n) on heap size
|
187
|
-
def sift_down(idx)
|
188
|
-
return self if idx >= @store.size
|
189
|
-
lidx, ridx = self.class.children_idx(idx)
|
190
|
-
# promote the heapiest child
|
191
|
-
cidx = self.heapish?(lidx, ridx) ? lidx : ridx
|
192
|
-
if !self.heapish?(idx, cidx)
|
193
|
-
@store[idx], @store[cidx] = @store[cidx], @store[idx] # swap
|
194
|
-
self.sift_down(cidx)
|
195
|
-
end
|
196
|
-
self
|
197
|
-
end
|
198
|
-
|
199
|
-
# are values of parent and child (by index) in accordance with heap property?
|
200
|
-
def heapish?(pidx, cidx)
|
201
|
-
(@store[pidx] <=> @store[cidx]) != (@cmp_val * -1)
|
202
|
-
end
|
203
|
-
|
204
|
-
# not used internally
|
205
|
-
# checks that every node satisfies the heap property
|
206
|
-
# calls heapish? on idx's children and then heap? on them recursively
|
207
|
-
def heap?(idx: 0)
|
208
|
-
check_children = []
|
209
|
-
self.class.children_idx(idx).each { |cidx|
|
210
|
-
# cidx is arithmetically produced; the corresponding child may not exist
|
211
|
-
if cidx < @store.size
|
117
|
+
if cidx < @array.size
|
212
118
|
return false unless self.heapish?(idx, cidx)
|
213
119
|
check_children << cidx
|
214
120
|
end
|