compsci 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 +7 -0
- data/README.md +48 -0
- data/Rakefile +108 -0
- data/VERSION +1 -0
- data/compsci.gemspec +38 -0
- data/examples/binary_tree.rb +21 -0
- data/examples/heap.rb +65 -0
- data/examples/timer.rb +42 -0
- data/lib/compsci/fib.rb +24 -0
- data/lib/compsci/fit.rb +137 -0
- data/lib/compsci/heap.rb +100 -0
- data/lib/compsci/timer.rb +36 -0
- data/lib/compsci/tree.rb +140 -0
- data/lib/compsci.rb +2 -0
- data/test/bench/fib.rb +114 -0
- data/test/bench/heap.rb +15 -0
- data/test/bench/tree.rb +19 -0
- data/test/fib.rb +13 -0
- data/test/fit.rb +162 -0
- data/test/heap.rb +46 -0
- data/test/timer.rb +48 -0
- data/test/tree.rb +209 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dc7af3ec0bc93ea91b485b329e66033cca137597
|
4
|
+
data.tar.gz: f62c24c3171a6534e92fa8f53dba942e009442c3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fe64178808bef0a929882318ed08941de2179940bc332ee42ca9f48f0430e566ae6299f628aa4ad11573787c59fe9746cd8a23b5309fa1ecd92117fd1c0daf16
|
7
|
+
data.tar.gz: 0ce9187b7eb0d17add238c6344452e344685e00126927359f2d2d54f6e3fbc8ce5a389d153027c7f626b8039f86e8b7409d1950f2d14afbd5619ec8dd878ff4c
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
[](https://travis-ci.org/rickhull/compsci)
|
2
|
+
|
3
|
+
# Introduction
|
4
|
+
|
5
|
+
Provided are some toy implementations for some basic computer science problems.
|
6
|
+
|
7
|
+
## [`Tree`](/lib/compsci/tree.rb) data structures
|
8
|
+
|
9
|
+
* `Tree` - enforces number of children per node
|
10
|
+
* `Tree::Node` - references parent and children nodes
|
11
|
+
* `BinaryTree` - subclass of `Tree`; child_slots == 2
|
12
|
+
* `CompleteBinaryTree` - efficient Array implementation
|
13
|
+
|
14
|
+
## [`Heap`](lib/compsci/heap.rb) data structure
|
15
|
+
|
16
|
+
Implemented with a `CompleteBinaryTree` for storage using simple arithmetic to
|
17
|
+
determine array indices for parent and children. See the
|
18
|
+
[heap example](https://github.com/rickhull/compsci/blob/master/eamples/heap.rb)
|
19
|
+
which can be executed (among other examples) via `rake examples`.
|
20
|
+
|
21
|
+
Both minheaps and maxheaps are supported. The primary operations are
|
22
|
+
`Heap#push` and `Heap#pop`. My basic Vagrant VM gets over 350k pushes per
|
23
|
+
second, constant up past 1M pushes.
|
24
|
+
|
25
|
+
## [`Fibonacci`](lib/compsci/fib.rb) functions
|
26
|
+
|
27
|
+
* `Fibonacci.classic(n)` - naive, recursive
|
28
|
+
* `Fibonacci.cache_recursive(n)` - as above, caching already computed results
|
29
|
+
* `Fibonacci.cache_iterative(n)` - as above but iterative
|
30
|
+
* `Fibonacci.dynamic(n)` - as above but without a cache structure
|
31
|
+
|
32
|
+
## [`Timer`](/lib/compsci/timer.rb) functions
|
33
|
+
|
34
|
+
* `Timer.now` - uses `Process::CLOCK_MONOTONIC` if available
|
35
|
+
* `Timer.elapsed` - provides the elapsed time to run a block
|
36
|
+
* `Timer.loop_average` - runs a block repeatedly and provides the mean elapsed
|
37
|
+
time
|
38
|
+
* `Timer.since` - provides the elapsed time since a prior time
|
39
|
+
|
40
|
+
## [`Fit`](lib/compsci/fit.rb) functions
|
41
|
+
|
42
|
+
* `Fit.sigma` - sums the result of a block applied to array values
|
43
|
+
* `Fit.error` - returns a generic r^2 value, the coefficient of determination
|
44
|
+
* `Fit.constant` - fits `y = a + 0x`; returns the mean and variance
|
45
|
+
* `Fit.logarithmic` - fits `y = a + b*ln(x)`; returns a, b, r^2
|
46
|
+
* `Fit.linear` - fits `y = a + bx`; returns a, b, r^2
|
47
|
+
* `Fit.exponential` fits `y = ae^(bx)`; returns a, b, r^2
|
48
|
+
* `Fit.power` fits `y = ax^b`; returns a, b, r^2
|
data/Rakefile
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
|
3
|
+
Rake::TestTask.new :test do |t|
|
4
|
+
t.pattern = "test/*.rb"
|
5
|
+
t.warning = true
|
6
|
+
end
|
7
|
+
|
8
|
+
Rake::TestTask.new bench: :test do |t|
|
9
|
+
t.pattern = "test/bench/*.rb"
|
10
|
+
t.warning = true
|
11
|
+
t.description = "Run benchmarks"
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Run example scripts"
|
15
|
+
task examples: :test do
|
16
|
+
Dir['examples/**/*.rb'].each { |filepath|
|
17
|
+
puts
|
18
|
+
sh "ruby -Ilib #{filepath}"
|
19
|
+
puts
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
task default: :examples
|
24
|
+
|
25
|
+
#
|
26
|
+
# METRICS
|
27
|
+
#
|
28
|
+
|
29
|
+
metrics_tasks = []
|
30
|
+
|
31
|
+
begin
|
32
|
+
require 'flog_task'
|
33
|
+
FlogTask.new do |t|
|
34
|
+
t.threshold = 420
|
35
|
+
t.dirs = ['lib']
|
36
|
+
t.verbose = true
|
37
|
+
end
|
38
|
+
metrics_tasks << :flog
|
39
|
+
rescue LoadError
|
40
|
+
warn 'flog_task unavailable'
|
41
|
+
end
|
42
|
+
|
43
|
+
begin
|
44
|
+
require 'flay_task'
|
45
|
+
FlayTask.new do |t|
|
46
|
+
t.dirs = ['lib']
|
47
|
+
t.verbose = true
|
48
|
+
end
|
49
|
+
metrics_tasks << :flay
|
50
|
+
rescue LoadError
|
51
|
+
warn 'flay_task unavailable'
|
52
|
+
end
|
53
|
+
|
54
|
+
begin
|
55
|
+
require 'roodi_task'
|
56
|
+
RoodiTask.new config: '.roodi.yml', patterns: ['lib/**/*.rb']
|
57
|
+
metrics_tasks << :roodi
|
58
|
+
rescue LoadError
|
59
|
+
warn "roodi_task unavailable"
|
60
|
+
end
|
61
|
+
|
62
|
+
desc "Generate code metrics reports"
|
63
|
+
task code_metrics: metrics_tasks
|
64
|
+
|
65
|
+
#
|
66
|
+
# PROFILING
|
67
|
+
#
|
68
|
+
|
69
|
+
desc "Show current system load"
|
70
|
+
task "loadavg" do
|
71
|
+
puts File.read "/proc/loadavg"
|
72
|
+
end
|
73
|
+
|
74
|
+
def lib_sh(cmd)
|
75
|
+
sh "RUBYLIB=lib #{cmd}"
|
76
|
+
end
|
77
|
+
|
78
|
+
def rprof_sh(script, args = '', rprof_args = '')
|
79
|
+
lib_sh ['ruby-prof', rprof_args, script, '--', args].join(' ')
|
80
|
+
end
|
81
|
+
|
82
|
+
scripts = ["examples/binary_tree.rb", "examples/heap.rb"]
|
83
|
+
|
84
|
+
desc "Run ruby-prof on examples/"
|
85
|
+
task "ruby-prof" => "loadavg" do
|
86
|
+
scripts.each { |script| rprof_sh script }
|
87
|
+
end
|
88
|
+
|
89
|
+
desc "Run ruby-prof on examples/ with --exclude-common-cycles"
|
90
|
+
task "ruby-prof-exclude" => "loadavg" do
|
91
|
+
scripts.each { |script| rprof_sh script, "", "--exclude-common-cycles" }
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# GEM BUILD / PUBLISH
|
96
|
+
#
|
97
|
+
|
98
|
+
begin
|
99
|
+
require 'buildar'
|
100
|
+
|
101
|
+
Buildar.new do |b|
|
102
|
+
b.gemspec_file = 'compsci.gemspec'
|
103
|
+
b.version_file = 'VERSION'
|
104
|
+
b.use_git = true
|
105
|
+
end
|
106
|
+
rescue LoadError
|
107
|
+
warn "buildar tasks unavailable"
|
108
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1.1
|
data/compsci.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'compsci'
|
3
|
+
s.summary = "Toy implementations for some basic computer science problems"
|
4
|
+
s.description = "Trees, Heaps, Timers, Error fitting, etc"
|
5
|
+
s.authors = ["Rick Hull"]
|
6
|
+
s.homepage = "https://github.com/rickhull/compsci"
|
7
|
+
s.license = "LGPL-3.0"
|
8
|
+
|
9
|
+
s.required_ruby_version = "~> 2"
|
10
|
+
|
11
|
+
s.version = File.read(File.join(__dir__, 'VERSION')).chomp
|
12
|
+
|
13
|
+
s.files = %w[
|
14
|
+
compsci.gemspec
|
15
|
+
VERSION
|
16
|
+
README.md
|
17
|
+
Rakefile
|
18
|
+
lib/compsci.rb
|
19
|
+
lib/compsci/fib.rb
|
20
|
+
lib/compsci/fit.rb
|
21
|
+
lib/compsci/heap.rb
|
22
|
+
lib/compsci/timer.rb
|
23
|
+
lib/compsci/tree.rb
|
24
|
+
examples/binary_tree.rb
|
25
|
+
examples/heap.rb
|
26
|
+
examples/timer.rb
|
27
|
+
test/fib.rb
|
28
|
+
test/fit.rb
|
29
|
+
test/heap.rb
|
30
|
+
test/timer.rb
|
31
|
+
test/tree.rb
|
32
|
+
test/bench/fib.rb
|
33
|
+
test/bench/heap.rb
|
34
|
+
test/bench/tree.rb
|
35
|
+
]
|
36
|
+
|
37
|
+
s.add_development_dependency "minitest", "~> 5.0"
|
38
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'compsci/tree'
|
2
|
+
|
3
|
+
include CompSci
|
4
|
+
|
5
|
+
vals = []
|
6
|
+
30.times { vals << rand(99) }
|
7
|
+
p vals
|
8
|
+
|
9
|
+
root_node = Tree::Node.new vals.shift
|
10
|
+
tree = BinaryTree.new(root_node)
|
11
|
+
tree.push vals.shift until vals.empty?
|
12
|
+
|
13
|
+
tree.bf_print
|
14
|
+
|
15
|
+
tree.df_search { |n|
|
16
|
+
puts "visited #{n}"
|
17
|
+
false # or n.value > 90
|
18
|
+
}
|
19
|
+
puts
|
20
|
+
|
21
|
+
p tree
|
data/examples/heap.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'compsci/heap'
|
2
|
+
require 'compsci/timer'
|
3
|
+
|
4
|
+
include CompSci
|
5
|
+
|
6
|
+
puts <<EOF
|
7
|
+
#
|
8
|
+
# 3 seconds worth of inserts
|
9
|
+
#
|
10
|
+
|
11
|
+
EOF
|
12
|
+
|
13
|
+
count = 0
|
14
|
+
start = Timer.now
|
15
|
+
h = Heap.new
|
16
|
+
elapsed = 0
|
17
|
+
|
18
|
+
while elapsed < 3
|
19
|
+
_answer, push_elapsed = Timer.elapsed { h.push rand 99999 }
|
20
|
+
count += 1
|
21
|
+
puts "%ith push: %0.8f s" % [count, push_elapsed] if count % 10000 == 0
|
22
|
+
|
23
|
+
if count % 100000 == 0
|
24
|
+
start_100k ||= start
|
25
|
+
push_100k_elapsed = Timer.now - start_100k
|
26
|
+
puts "-------------"
|
27
|
+
puts " 100k push: %0.8f s (%ik push / s)" %
|
28
|
+
[push_100k_elapsed, 100.to_f / push_100k_elapsed]
|
29
|
+
puts
|
30
|
+
start_100k = Timer.now
|
31
|
+
end
|
32
|
+
elapsed = Timer.now - start
|
33
|
+
end
|
34
|
+
|
35
|
+
puts "pushed %i items in %0.1f s" % [count, elapsed]
|
36
|
+
puts
|
37
|
+
|
38
|
+
print "still a heap with #{h.size} items? "
|
39
|
+
answer, elapsed = Timer.elapsed { h.heap? }
|
40
|
+
puts "%s - %0.3f sec" % [answer ? 'YES' : 'NO', elapsed]
|
41
|
+
puts
|
42
|
+
|
43
|
+
puts <<EOF
|
44
|
+
#
|
45
|
+
# 99 inserts; display the internal array
|
46
|
+
#
|
47
|
+
|
48
|
+
EOF
|
49
|
+
|
50
|
+
h = Heap.new
|
51
|
+
|
52
|
+
puts "push: %s" % Array.new(99) { rand(99).tap { |i| h.push i } }.join(' ')
|
53
|
+
puts "heap store: #{h.store.inspect}"
|
54
|
+
puts "heap: #{h.heap?}"
|
55
|
+
puts
|
56
|
+
|
57
|
+
puts "pop: %i" % h.pop
|
58
|
+
puts "heap store: #{h.store.inspect}"
|
59
|
+
puts "heap: #{h.heap?}"
|
60
|
+
puts
|
61
|
+
|
62
|
+
puts "pop: %s" % Array.new(9) { h.pop }.join(' ')
|
63
|
+
puts "heap store: #{h.store.inspect}"
|
64
|
+
puts "heap: #{h.heap?}"
|
65
|
+
puts
|
data/examples/timer.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'compsci/timer'
|
2
|
+
|
3
|
+
include CompSci
|
4
|
+
|
5
|
+
overall_start = Timer.now
|
6
|
+
|
7
|
+
start = Timer.now
|
8
|
+
print "running sleep 0.01 (50x): "
|
9
|
+
_answer, each_et = Timer.loop_average(count: 50) {
|
10
|
+
print '.'
|
11
|
+
sleep 0.01
|
12
|
+
}
|
13
|
+
puts
|
14
|
+
puts "each: %0.3f" % each_et
|
15
|
+
puts "elapsed: %0.3f" % Timer.since(start)
|
16
|
+
puts "cumulative: %0.3f" % Timer.since(overall_start)
|
17
|
+
puts
|
18
|
+
|
19
|
+
|
20
|
+
start = Timer.now
|
21
|
+
print "running sleep 0.02 (0.5 s): "
|
22
|
+
_answer, each_et = Timer.loop_average(seconds: 0.5) {
|
23
|
+
print '.'
|
24
|
+
sleep 0.02
|
25
|
+
}
|
26
|
+
puts
|
27
|
+
puts "each: %0.3f" % each_et
|
28
|
+
puts "elapsed: %0.3f" % Timer.since(start)
|
29
|
+
puts "cumulative: %0.3f" % Timer.since(overall_start)
|
30
|
+
puts
|
31
|
+
|
32
|
+
|
33
|
+
start = Timer.now
|
34
|
+
print "running sleep 2 (1 s): "
|
35
|
+
_answer, each_et = Timer.loop_average(seconds: 1) {
|
36
|
+
print '.'
|
37
|
+
sleep 2
|
38
|
+
}
|
39
|
+
puts
|
40
|
+
puts "each: %0.3f" % each_et
|
41
|
+
puts "elapsed: %0.3f" % Timer.since(start)
|
42
|
+
puts "cumulative: %0.3f" % Timer.since(overall_start)
|
data/lib/compsci/fib.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'compsci'
|
2
|
+
|
3
|
+
module CompSci::Fibonacci
|
4
|
+
def self.classic(n)
|
5
|
+
n < 2 ? n : classic(n-1) + classic(n-2)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.cache_recursive(n, cache = {})
|
9
|
+
return n if n < 2
|
10
|
+
cache[n] ||= cache_recursive(n-1, cache) + cache_recursive(n-2, cache)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.cache_iterative(n)
|
14
|
+
cache = [0, 1]
|
15
|
+
2.upto(n) { |i| cache[i] = cache[i-1] + cache[i-2] }
|
16
|
+
cache[n]
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.dynamic(n)
|
20
|
+
a, b = 0, 1
|
21
|
+
(n-1).times { a, b = b, a+b }
|
22
|
+
b
|
23
|
+
end
|
24
|
+
end
|
data/lib/compsci/fit.rb
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'compsci'
|
2
|
+
|
3
|
+
module CompSci::Fit
|
4
|
+
#
|
5
|
+
# functions below originally copied from https://github.com/seattlrb/minitest
|
6
|
+
#
|
7
|
+
|
8
|
+
##
|
9
|
+
# Enumerates over +enum+ mapping +block+ if given, returning the
|
10
|
+
# sum of the result. Eg:
|
11
|
+
#
|
12
|
+
# sigma([1, 2, 3]) # => 1 + 2 + 3 => 7
|
13
|
+
# sigma([1, 2, 3]) { |n| n ** 2 } # => 1 + 4 + 9 => 14
|
14
|
+
|
15
|
+
def self.sigma enum, &block
|
16
|
+
enum = enum.map(&block) if block
|
17
|
+
enum.inject { |sum, n| sum + n }
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Takes an array of x/y pairs and calculates the general R^2 value to
|
22
|
+
# measure fit against a predictive function, which is the block supplied
|
23
|
+
# to error:
|
24
|
+
#
|
25
|
+
# e.g. error(xys) { |x| 5 + 2 * x }
|
26
|
+
#
|
27
|
+
# See: http://en.wikipedia.org/wiki/Coefficient_of_determination
|
28
|
+
#
|
29
|
+
|
30
|
+
def self.error xys, &blk
|
31
|
+
y_bar = sigma(xys) { |_, y| y } / xys.size.to_f
|
32
|
+
ss_tot = sigma(xys) { |_, y| (y - y_bar) ** 2 }
|
33
|
+
ss_res = sigma(xys) { |x, y| (yield(x) - y) ** 2 }
|
34
|
+
|
35
|
+
1 - (ss_res / ss_tot)
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Fits the functional form: a (+ 0x)
|
40
|
+
#
|
41
|
+
# Takes x and y values and returns [a, variance]
|
42
|
+
#
|
43
|
+
|
44
|
+
def self.constant xs, ys
|
45
|
+
# written by Rick
|
46
|
+
y_bar = sigma(ys) / ys.size.to_f
|
47
|
+
variance = sigma(ys) { |y| (y - y_bar) ** 2 }
|
48
|
+
[y_bar, variance]
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# To fit a functional form: y = a + b*ln(x).
|
53
|
+
#
|
54
|
+
# Takes x and y values and returns [a, b, r^2].
|
55
|
+
#
|
56
|
+
# See: http://mathworld.wolfram.com/LeastSquaresFittingLogarithmic.html
|
57
|
+
|
58
|
+
def self.logarithmic xs, ys
|
59
|
+
n = xs.size
|
60
|
+
xys = xs.zip(ys)
|
61
|
+
slnx2 = sigma(xys) { |x, _| Math.log(x) ** 2 }
|
62
|
+
slnx = sigma(xys) { |x, _| Math.log(x) }
|
63
|
+
sylnx = sigma(xys) { |x, y| y * Math.log(x) }
|
64
|
+
sy = sigma(xys) { |_, y| y }
|
65
|
+
|
66
|
+
c = n * slnx2 - slnx ** 2
|
67
|
+
b = ( n * sylnx - sy * slnx ) / c
|
68
|
+
a = (sy - b * slnx) / n
|
69
|
+
|
70
|
+
return a, b, self.error(xys) { |x| a + b * Math.log(x) }
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Fits the functional form: a + bx.
|
75
|
+
#
|
76
|
+
# Takes x and y values and returns [a, b, r^2].
|
77
|
+
#
|
78
|
+
# See: http://mathworld.wolfram.com/LeastSquaresFitting.html
|
79
|
+
|
80
|
+
def self.linear xs, ys
|
81
|
+
n = xs.size
|
82
|
+
xys = xs.zip(ys)
|
83
|
+
sx = sigma xs
|
84
|
+
sy = sigma ys
|
85
|
+
sx2 = sigma(xs) { |x| x ** 2 }
|
86
|
+
sxy = sigma(xys) { |x, y| x * y }
|
87
|
+
|
88
|
+
c = n * sx2 - sx**2
|
89
|
+
a = (sy * sx2 - sx * sxy) / c
|
90
|
+
b = ( n * sxy - sx * sy ) / c
|
91
|
+
|
92
|
+
return a, b, self.error(xys) { |x| a + b * x }
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# To fit a functional form: y = ae^(bx).
|
97
|
+
#
|
98
|
+
# Takes x and y values and returns [a, b, r^2].
|
99
|
+
#
|
100
|
+
# See: http://mathworld.wolfram.com/LeastSquaresFittingExponential.html
|
101
|
+
|
102
|
+
def self.exponential xs, ys
|
103
|
+
n = xs.size
|
104
|
+
xys = xs.zip(ys)
|
105
|
+
sxlny = sigma(xys) { |x, y| x * Math.log(y) }
|
106
|
+
slny = sigma(xys) { |_, y| Math.log(y) }
|
107
|
+
sx2 = sigma(xys) { |x, _| x * x }
|
108
|
+
sx = sigma xs
|
109
|
+
|
110
|
+
c = n * sx2 - sx ** 2
|
111
|
+
a = (slny * sx2 - sx * sxlny) / c
|
112
|
+
b = ( n * sxlny - sx * slny ) / c
|
113
|
+
|
114
|
+
return Math.exp(a), b, self.error(xys) { |x| Math.exp(a + b * x) }
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# To fit a functional form: y = ax^b.
|
119
|
+
#
|
120
|
+
# Takes x and y values and returns [a, b, r^2].
|
121
|
+
#
|
122
|
+
# See: http://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html
|
123
|
+
|
124
|
+
def self.power xs, ys
|
125
|
+
n = xs.size
|
126
|
+
xys = xs.zip(ys)
|
127
|
+
slnxlny = sigma(xys) { |x, y| Math.log(x) * Math.log(y) }
|
128
|
+
slnx = sigma(xs) { |x | Math.log(x) }
|
129
|
+
slny = sigma(ys) { | y| Math.log(y) }
|
130
|
+
slnx2 = sigma(xs) { |x | Math.log(x) ** 2 }
|
131
|
+
|
132
|
+
b = (n * slnxlny - slnx * slny) / (n * slnx2 - slnx ** 2)
|
133
|
+
a = (slny - b * slnx) / n
|
134
|
+
|
135
|
+
return Math.exp(a), b, self.error(xys) { |x| (Math.exp(a) * (x ** b)) }
|
136
|
+
end
|
137
|
+
end
|
data/lib/compsci/heap.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'compsci/tree'
|
2
|
+
|
3
|
+
# A Heap is a partially sorted, complete binary tree with the property:
|
4
|
+
# * Every node has a value larger (or smaller) than that of its children.
|
5
|
+
#
|
6
|
+
# This class implements a heap using a simple array for storage.
|
7
|
+
# Array index math is used to find:
|
8
|
+
# * The root node (idx 0)
|
9
|
+
# * The "bottom-most" leaf node (last idx)
|
10
|
+
# * Parent idx (idx-1 / 2)
|
11
|
+
# * Child idx (2*idx + 1, 2*idx + 2)
|
12
|
+
#
|
13
|
+
# Any Comparable may be used for node values.
|
14
|
+
# Initialize a heap with a cmp_val, either 1 for a MaxHeap or -1 for a MinHeap.
|
15
|
+
# The heap property is satisfied when a parent value equals a child value.
|
16
|
+
# Insertion (push) and removal (pop) are O(log n) where n is the heap size.
|
17
|
+
# Nodes are inserted at the end of the array, and sift_up is called to
|
18
|
+
# reestablish the heap property.
|
19
|
+
# Nodes are removed from the start of the array, and sift_down is called to
|
20
|
+
# reestablish the heap property.
|
21
|
+
# Sift_up and sift_down are O(log n) because they only have to check and swap
|
22
|
+
# nodes at each layer of the tree, and there are log n layers to the tree.
|
23
|
+
#
|
24
|
+
class CompSci::Heap < CompSci::CompleteBinaryTree
|
25
|
+
# defaults to a MaxHeap, with the largest node at the root
|
26
|
+
# specify a minheap with minheap: true or cmp_val: -1
|
27
|
+
#
|
28
|
+
def initialize(cmp_val: 1, minheap: false)
|
29
|
+
super()
|
30
|
+
cmp_val = -1 if minheap
|
31
|
+
case cmp_val
|
32
|
+
when -1, 1
|
33
|
+
@cmp_val = cmp_val
|
34
|
+
else
|
35
|
+
raise(ArgumentError, "unknown comparison value: #{cmp_val}")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# append to the array; sift_up
|
40
|
+
def push(node)
|
41
|
+
@store << node
|
42
|
+
self.sift_up(@store.size - 1)
|
43
|
+
end
|
44
|
+
|
45
|
+
# remove from the front of the array; move last node to root; sift_down
|
46
|
+
def pop
|
47
|
+
node = @store.shift
|
48
|
+
replacement = @store.pop
|
49
|
+
@store.unshift replacement if replacement
|
50
|
+
self.sift_down(0)
|
51
|
+
node
|
52
|
+
end
|
53
|
+
|
54
|
+
# return what pop would return (avoid sifting)
|
55
|
+
def peek
|
56
|
+
@store.first
|
57
|
+
end
|
58
|
+
|
59
|
+
# called recursively; idx represents the node suspected to violate the heap
|
60
|
+
def sift_up(idx)
|
61
|
+
return self if idx <= 0
|
62
|
+
pidx = self.class.parent_idx(idx)
|
63
|
+
if !self.heapish?(pidx, idx)
|
64
|
+
@store[idx], @store[pidx] = @store[pidx], @store[idx] # swap
|
65
|
+
self.sift_up(pidx)
|
66
|
+
end
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
# called recursively; idx represents the node suspected to violate the heap
|
71
|
+
def sift_down(idx)
|
72
|
+
return self if idx >= @store.size
|
73
|
+
lidx, ridx = self.class.children_idx(idx)
|
74
|
+
# take the child most likely to be a good parent
|
75
|
+
cidx = self.heapish?(lidx, ridx) ? lidx : ridx
|
76
|
+
if !self.heapish?(idx, cidx)
|
77
|
+
@store[idx], @store[cidx] = @store[cidx], @store[idx] # swap
|
78
|
+
self.sift_down(cidx)
|
79
|
+
end
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
# are values of parent and child (by index) in accordance with heap property?
|
84
|
+
def heapish?(pidx, cidx)
|
85
|
+
(@store[pidx] <=> @store[cidx]) != (@cmp_val * -1)
|
86
|
+
end
|
87
|
+
|
88
|
+
# not used internally; checks that every node satisfies the heap property
|
89
|
+
def heap?(idx: 0)
|
90
|
+
check_children = []
|
91
|
+
self.class.children_idx(idx).each { |cidx|
|
92
|
+
if cidx < @store.size
|
93
|
+
return false unless self.heapish?(idx, cidx)
|
94
|
+
check_children << cidx
|
95
|
+
end
|
96
|
+
}
|
97
|
+
check_children.each { |cidx| return false unless self.heap?(idx: cidx) }
|
98
|
+
true
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'compsci'
|
2
|
+
|
3
|
+
module CompSci::Timer
|
4
|
+
# lifted from seattlerb/minitest
|
5
|
+
if defined? Process::CLOCK_MONOTONIC
|
6
|
+
def self.now
|
7
|
+
Process.clock_gettime Process::CLOCK_MONOTONIC
|
8
|
+
end
|
9
|
+
else
|
10
|
+
def self.now
|
11
|
+
Time.now
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.since(t)
|
16
|
+
self.now - t
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.elapsed(&work)
|
20
|
+
t = self.now
|
21
|
+
return yield, self.since(t)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.loop_average(count: 999, seconds: 1, &work)
|
25
|
+
i = 0
|
26
|
+
start = self.now
|
27
|
+
val = nil
|
28
|
+
loop {
|
29
|
+
val = yield
|
30
|
+
i += 1
|
31
|
+
break if i >= count
|
32
|
+
break if self.since(start) > seconds
|
33
|
+
}
|
34
|
+
return val, self.since(start) / i.to_f
|
35
|
+
end
|
36
|
+
end
|