compsci 0.0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/rickhull/compsci.svg?branch=master)](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
|