compsci 0.0.3.1 → 0.1.0.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 +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
|