compsci 0.1.1.1 → 0.2.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 +74 -5
- data/Rakefile +20 -9
- data/VERSION +1 -1
- data/compsci.gemspec +1 -0
- data/examples/binary_search_tree.rb +16 -0
- data/examples/heap.rb +0 -42
- data/examples/heap_push.rb +46 -0
- data/examples/tree.rb +2 -1
- data/examples/{binary_tree.rb → tree_push.rb} +3 -2
- data/lib/compsci/binary_search_tree.rb +86 -0
- data/lib/compsci/fibonacci.rb +1 -9
- data/lib/compsci/fit.rb +34 -14
- data/lib/compsci/names.rb +3 -4
- data/lib/compsci/node.rb +66 -19
- data/lib/compsci/simplex.rb +173 -0
- data/lib/compsci/simplex/parse.rb +125 -0
- data/lib/compsci/tree.rb +14 -1
- data/test/bench/complete_tree.rb +59 -0
- data/test/bench/fibonacci.rb +0 -4
- data/test/bench/simplex.rb +141 -0
- data/test/bench/tree.rb +20 -15
- data/test/binary_search_tree.rb +106 -0
- data/test/fit.rb +5 -11
- data/test/node.rb +55 -4
- data/test/simplex.rb +291 -0
- data/test/simplex_parse.rb +94 -0
- data/test/tree.rb +33 -9
- metadata +27 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3fa74f40bbc97fa685c811d2b15738da31d69c66
|
4
|
+
data.tar.gz: '02965023a412fae230e7fe3e42704982c6410bab'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4832093f2d37d27b55f2b32fe20422704100d7cd515d51790d745dbf22d7006866959dc844939d2cff19e60d3ee54b78100a9785511274ed43aa22dd86a7fd8
|
7
|
+
data.tar.gz: 5b5ad3eeedadddd92abca745bf7822dbd9cff07fa79ccc6d34cae075d6a1c24dbc747aa1939d44806e1ad83b914b9e0b880fc6be26c505c98f869fa3dd1ff9f6
|
data/README.md
CHANGED
@@ -9,10 +9,21 @@ Provided are some toy implementations for some basic computer science problems.
|
|
9
9
|
* `Node`
|
10
10
|
- `@value`
|
11
11
|
- `@children`
|
12
|
-
|
12
|
+
- `#set_child(idx, node)`
|
13
|
+
* `KeyNode` inherits from `Node`; adds a key
|
14
|
+
- `@key`
|
15
|
+
* `FlexNode` inherits from `Node`; accumulates children
|
16
|
+
- `#add_child(node)`
|
17
|
+
- `#new_child(value)`
|
18
|
+
- `#add_parent(node)`
|
19
|
+
* `ChildNode` inherits from `Node`; adds a parent
|
13
20
|
- `@parent`
|
14
21
|
- `#gen`
|
15
22
|
- `#siblings`
|
23
|
+
* `ChildFlexNode` inherits from `ChildNode`; accumulates children
|
24
|
+
- `#add_child(node)`
|
25
|
+
- `#new_child(value)`
|
26
|
+
- `#add_parent(node)`
|
16
27
|
|
17
28
|
## [`Tree`](lib/compsci/tree.rb) data structures
|
18
29
|
|
@@ -55,16 +66,21 @@ Efficient Array implementation of a complete tree.
|
|
55
66
|
* `CompleteQuaternaryTree`
|
56
67
|
- `CompleteNaryTree.new(child_slots: 4)`
|
57
68
|
|
69
|
+
## [`BinarySearchTree`](lib/compsci/binary_search_tree.rb) data structure
|
70
|
+
|
71
|
+
Based on `BinaryTree` with `KeyNode`s. The position of a node depends on its
|
72
|
+
key and how the key relates to the existing node keys.
|
73
|
+
|
58
74
|
## [`Heap`](lib/compsci/heap.rb) data structure
|
59
75
|
|
60
76
|
`CompleteNaryTree` implementation. Both minheaps and maxheaps are supported.
|
61
77
|
Any number of children may be provided via `child_slots`. The primary
|
62
78
|
operations are `Heap#push` and `Heap#pop`. See the
|
63
|
-
[heap
|
64
|
-
via `rake examples`.
|
79
|
+
[heap](examples/heap.rb) [examples](examples/heap_push.rb)
|
80
|
+
which can be executed (among other examples) via `rake examples`.
|
65
81
|
|
66
|
-
My basic Vagrant VM gets
|
67
|
-
pushes.
|
82
|
+
My basic Vagrant VM gets over [500k pushes per second, constant up past 1M
|
83
|
+
pushes](reports/examples#L484).
|
68
84
|
|
69
85
|
## [`Fibonacci`](lib/compsci/fibonacci.rb) functions
|
70
86
|
|
@@ -142,3 +158,56 @@ cumulative: 0.828
|
|
142
158
|
* `Names::Greek.upper`
|
143
159
|
* `Names::Greek.lower`
|
144
160
|
* `Names::Greek.symbol`
|
161
|
+
|
162
|
+
## [`Simplex`](lib/compsci/simplex.rb) class
|
163
|
+
|
164
|
+
The Simplex algorithm is a technique for Linear Programming. Typically the
|
165
|
+
problem is to maximize some linear expression of variables given some
|
166
|
+
constraints on those variables given in terms of linear inequalities.
|
167
|
+
|
168
|
+
### [`Simplex::Parse`](lib/compsci/simplex/parse.rb) functions
|
169
|
+
|
170
|
+
* `Parse.tokenize` - convert a string to an array of tokens
|
171
|
+
* `Parse.term` - parse certain tokens into [coefficient, varname]
|
172
|
+
* `Parse.expression` - parse a string representing a sum of terms
|
173
|
+
* `Parse.inequality` - parse a string like "#{expression} <= #{const}"
|
174
|
+
|
175
|
+
With `Simplex::Parse`, one can obtain solutions via:
|
176
|
+
|
177
|
+
* `Simplex.maximize` - takes an expression to maximize followed by a variable
|
178
|
+
number of constraints / inequalities; returns a solution
|
179
|
+
* `Simplex.problem` - a more general form of `Simplex.maximize`; returns a
|
180
|
+
Simplex object
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
require 'compsci/simplex/parse'
|
184
|
+
|
185
|
+
include CompSci
|
186
|
+
|
187
|
+
Simplex.maximize('x + y',
|
188
|
+
'2x + y <= 4',
|
189
|
+
'x + 2y <= 3')
|
190
|
+
|
191
|
+
# => [1.6666666666666667, 0.6666666666666666]
|
192
|
+
|
193
|
+
|
194
|
+
|
195
|
+
s = Simplex.problem(maximize: 'x + y',
|
196
|
+
constraints: ['2x + y <= 4',
|
197
|
+
'x + 2y <= 3'])
|
198
|
+
|
199
|
+
# => #<CompSci::Simplex:0x0055b2deadbeef
|
200
|
+
# @max_pivots=10000,
|
201
|
+
# @num_non_slack_vars=2,
|
202
|
+
# @num_constraints=2,
|
203
|
+
# @num_vars=4,
|
204
|
+
# @c=[-1.0, -1.0, 0, 0],
|
205
|
+
# @a=[[2.0, 1.0, 1, 0], [1.0, 2.0, 0, 1]],
|
206
|
+
# @b=[4.0, 3.0],
|
207
|
+
# @basic_vars=[2, 3],
|
208
|
+
# @x=[0, 0, 4.0, 3.0]>
|
209
|
+
|
210
|
+
s.solution
|
211
|
+
|
212
|
+
# => [1.6666666666666667, 0.6666666666666666]
|
213
|
+
```
|
data/Rakefile
CHANGED
@@ -22,20 +22,18 @@ end
|
|
22
22
|
|
23
23
|
task default: :examples
|
24
24
|
|
25
|
+
|
25
26
|
#
|
26
27
|
# METRICS
|
27
28
|
#
|
28
29
|
|
29
|
-
metrics_tasks = []
|
30
|
-
|
31
30
|
begin
|
32
31
|
require 'flog_task'
|
33
32
|
FlogTask.new do |t|
|
34
|
-
t.threshold =
|
33
|
+
t.threshold = 9000
|
35
34
|
t.dirs = ['lib']
|
36
35
|
t.verbose = true
|
37
36
|
end
|
38
|
-
metrics_tasks << :flog
|
39
37
|
rescue LoadError
|
40
38
|
warn 'flog_task unavailable'
|
41
39
|
end
|
@@ -46,7 +44,6 @@ begin
|
|
46
44
|
t.dirs = ['lib']
|
47
45
|
t.verbose = true
|
48
46
|
end
|
49
|
-
metrics_tasks << :flay
|
50
47
|
rescue LoadError
|
51
48
|
warn 'flay_task unavailable'
|
52
49
|
end
|
@@ -54,13 +51,10 @@ end
|
|
54
51
|
begin
|
55
52
|
require 'roodi_task'
|
56
53
|
RoodiTask.new config: '.roodi.yml', patterns: ['lib/**/*.rb']
|
57
|
-
metrics_tasks << :roodi
|
58
54
|
rescue LoadError
|
59
55
|
warn "roodi_task unavailable"
|
60
56
|
end
|
61
57
|
|
62
|
-
desc "Generate code metrics reports"
|
63
|
-
task code_metrics: metrics_tasks
|
64
58
|
|
65
59
|
#
|
66
60
|
# PROFILING
|
@@ -80,10 +74,11 @@ def rprof_sh(script, args = '', rprof_args = '')
|
|
80
74
|
end
|
81
75
|
|
82
76
|
scripts = [
|
83
|
-
"examples/binary_tree.rb",
|
84
77
|
"examples/complete_tree.rb",
|
85
78
|
"examples/heap.rb",
|
79
|
+
"examples/heap_push.rb",
|
86
80
|
"examples/tree.rb",
|
81
|
+
"examples/tree_push.rb",
|
87
82
|
]
|
88
83
|
|
89
84
|
desc "Run ruby-prof on examples/"
|
@@ -96,6 +91,22 @@ task "ruby-prof-exclude" => "loadavg" do
|
|
96
91
|
scripts.each { |script| rprof_sh script, "", "--exclude-common-cycles" }
|
97
92
|
end
|
98
93
|
|
94
|
+
|
95
|
+
#
|
96
|
+
# REPORTS
|
97
|
+
#
|
98
|
+
|
99
|
+
|
100
|
+
desc "Generate code metrics and reports"
|
101
|
+
task report: :test do
|
102
|
+
%w{examples bench flog flay roodi ruby-prof ruby-prof-exclude}.each { |t|
|
103
|
+
sh "rake #{t} > reports/#{t} 2>&1" do |ok, _status|
|
104
|
+
puts "rake #{t} failed" unless ok
|
105
|
+
end
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
|
99
110
|
#
|
100
111
|
# GEM BUILD / PUBLISH
|
101
112
|
#
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0.1
|
data/compsci.gemspec
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'compsci/node'
|
2
|
+
require 'compsci/binary_search_tree'
|
3
|
+
require 'compsci/names'
|
4
|
+
|
5
|
+
include CompSci
|
6
|
+
|
7
|
+
RANDMAX = 99
|
8
|
+
|
9
|
+
p vals = Names::WW1.shuffle
|
10
|
+
p keys = Array.new(vals.size) { rand RANDMAX }
|
11
|
+
|
12
|
+
root = BinarySearchTree.new_node(keys.shift, vals.shift)
|
13
|
+
tree = BinarySearchTree.new(root)
|
14
|
+
tree[keys.shift] = vals.shift until keys.empty?
|
15
|
+
# tree.insert(keys.shift, vals.shift) until keys.empty?
|
16
|
+
puts tree
|
data/examples/heap.rb
CHANGED
@@ -3,48 +3,6 @@ require 'compsci/timer'
|
|
3
3
|
|
4
4
|
include CompSci
|
5
5
|
|
6
|
-
puts <<EOF
|
7
|
-
#
|
8
|
-
# 3 seconds worth of pushes
|
9
|
-
#
|
10
|
-
|
11
|
-
EOF
|
12
|
-
|
13
|
-
count = 0
|
14
|
-
start = Timer.now
|
15
|
-
start_100k = Timer.now
|
16
|
-
h = Heap.new
|
17
|
-
|
18
|
-
loop {
|
19
|
-
count += 1
|
20
|
-
|
21
|
-
if count % 10000 == 0
|
22
|
-
_answer, push_elapsed = Timer.elapsed { h.push rand 99999 }
|
23
|
-
puts "%ith push: %0.8f s" % [count, push_elapsed]
|
24
|
-
if count % 100000 == 0
|
25
|
-
h.push rand 99999
|
26
|
-
push_100k_elapsed = Timer.since start_100k
|
27
|
-
puts "-------------"
|
28
|
-
puts " 100k push: %0.8f s (%ik push / s)" %
|
29
|
-
[push_100k_elapsed, 100.to_f / push_100k_elapsed]
|
30
|
-
puts
|
31
|
-
start_100k = Timer.now
|
32
|
-
end
|
33
|
-
else
|
34
|
-
h.push rand 99999
|
35
|
-
end
|
36
|
-
|
37
|
-
break if Timer.since(start) > 3
|
38
|
-
}
|
39
|
-
|
40
|
-
puts "pushed %i items in %0.1f s" % [count, Timer.since(start)]
|
41
|
-
puts
|
42
|
-
|
43
|
-
print "still a heap with #{h.size} items? "
|
44
|
-
answer, elapsed = Timer.elapsed { h.heap? }
|
45
|
-
puts "%s - %0.3f sec" % [answer ? 'YES' : 'NO', elapsed]
|
46
|
-
puts
|
47
|
-
|
48
6
|
puts <<EOF
|
49
7
|
#
|
50
8
|
# display the results of TernaryHeap push and pop
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'compsci/heap'
|
2
|
+
require 'compsci/timer'
|
3
|
+
|
4
|
+
include CompSci
|
5
|
+
|
6
|
+
puts <<EOF
|
7
|
+
#
|
8
|
+
# 3 seconds worth of pushes
|
9
|
+
#
|
10
|
+
|
11
|
+
EOF
|
12
|
+
|
13
|
+
count = 0
|
14
|
+
start = Timer.now
|
15
|
+
start_100k = Timer.now
|
16
|
+
h = Heap.new
|
17
|
+
|
18
|
+
loop {
|
19
|
+
count += 1
|
20
|
+
|
21
|
+
if count % 10000 == 0
|
22
|
+
_answer, push_elapsed = Timer.elapsed { h.push rand 99999 }
|
23
|
+
puts "%ith push: %0.8f s" % [count, push_elapsed]
|
24
|
+
if count % 100000 == 0
|
25
|
+
h.push rand 99999
|
26
|
+
push_100k_elapsed = Timer.since start_100k
|
27
|
+
puts "-------------"
|
28
|
+
puts " 100k push: %0.8f s (%ik push / s)" %
|
29
|
+
[push_100k_elapsed, 100.to_f / push_100k_elapsed]
|
30
|
+
puts
|
31
|
+
start_100k = Timer.now
|
32
|
+
end
|
33
|
+
else
|
34
|
+
h.push rand 99999
|
35
|
+
end
|
36
|
+
|
37
|
+
break if Timer.since(start) > 3
|
38
|
+
}
|
39
|
+
|
40
|
+
puts "pushed %i items in %0.1f s" % [count, Timer.since(start)]
|
41
|
+
puts
|
42
|
+
|
43
|
+
print "still a heap with #{h.size} items? "
|
44
|
+
answer, elapsed = Timer.elapsed { h.heap? }
|
45
|
+
puts "%s - %0.3f sec" % [answer ? 'YES' : 'NO', elapsed]
|
46
|
+
puts
|
data/examples/tree.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'compsci/node'
|
1
2
|
require 'compsci/tree'
|
2
3
|
require 'compsci/timer'
|
3
4
|
|
@@ -16,7 +17,7 @@ vals = Array.new(30) { rand 99 }
|
|
16
17
|
# start with the same vals for each class
|
17
18
|
my_vals = vals.dup
|
18
19
|
p my_vals
|
19
|
-
tree = tree_class.new(
|
20
|
+
tree = tree_class.new(ChildFlexNode, my_vals.shift)
|
20
21
|
tree.push my_vals.shift until my_vals.empty?
|
21
22
|
p tree
|
22
23
|
puts tree.display(width: 80)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'compsci/node'
|
1
2
|
require 'compsci/tree'
|
2
3
|
require 'compsci/timer'
|
3
4
|
|
@@ -13,7 +14,7 @@ EOF
|
|
13
14
|
count = 0
|
14
15
|
start = Timer.now
|
15
16
|
start_1k = Timer.now
|
16
|
-
tree = BinaryTree.new
|
17
|
+
tree = BinaryTree.new ChildFlexNode, rand(99)
|
17
18
|
|
18
19
|
loop {
|
19
20
|
count += 1
|
@@ -50,7 +51,7 @@ EOF
|
|
50
51
|
vals = Array.new(30) { rand 99 }
|
51
52
|
p vals
|
52
53
|
|
53
|
-
tree = BinaryTree.new
|
54
|
+
tree = BinaryTree.new ChildFlexNode, vals.shift
|
54
55
|
tree.push vals.shift until vals.empty?
|
55
56
|
puts tree
|
56
57
|
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'compsci/node'
|
2
|
+
require 'compsci/tree'
|
3
|
+
|
4
|
+
module CompSci
|
5
|
+
class BinarySearchTree < BinaryTree
|
6
|
+
def self.display_level(nodes: [], width: 80)
|
7
|
+
unless self.power_of?(nodes.size, 2)
|
8
|
+
raise "unexpected node count: #{nodes.size}"
|
9
|
+
end
|
10
|
+
block_width = [width / nodes.size, 1].max
|
11
|
+
nodes.map { |node|
|
12
|
+
str = node ? node.to_s : '_'
|
13
|
+
space = [(block_width + str.size) / 2, str.size + 1].max
|
14
|
+
str.ljust(space, ' ').rjust(block_width, ' ')
|
15
|
+
}.join
|
16
|
+
end
|
17
|
+
|
18
|
+
# helper method; any object which responds to key, value, and children
|
19
|
+
# may be used
|
20
|
+
def self.new_node(key, val)
|
21
|
+
CompSci::KeyNode.new(val, key: key, children: 2)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.new_with_kv(key, val)
|
25
|
+
self.new(self.new_node(key, val))
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(root_node)
|
29
|
+
@node_class = root_node.class
|
30
|
+
@child_slots = 2
|
31
|
+
if root_node.children.size == @child_slots
|
32
|
+
@root = root_node
|
33
|
+
else
|
34
|
+
raise "bad root: #{root_node}; expected #{@child_slots} child slots"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def search_recursive(key, node: @root)
|
39
|
+
return node if node.nil? or node.key == key
|
40
|
+
child = key < node.key ? node.children[0] : node.children[1]
|
41
|
+
search_recursive(key, node: child)
|
42
|
+
end
|
43
|
+
|
44
|
+
def search_iterative(key, node: @root)
|
45
|
+
while !node.nil?
|
46
|
+
return node if node.key == key
|
47
|
+
node = key < node.key ? node.children[0] : node.children[1]
|
48
|
+
end
|
49
|
+
node
|
50
|
+
end
|
51
|
+
|
52
|
+
def insert_recursive(key, val, node: @root)
|
53
|
+
return nil if node.nil? or node.key == key
|
54
|
+
if key < node.key
|
55
|
+
if node.children[0]
|
56
|
+
insert_recursive(key, val, node: node.children[0])
|
57
|
+
else
|
58
|
+
node.children[0] = @node_class.new(val, key: key, children: 2)
|
59
|
+
end
|
60
|
+
else
|
61
|
+
if node.children[1]
|
62
|
+
insert_recursive(key, val, node: node.children[1])
|
63
|
+
else
|
64
|
+
node.children[1] = @node_class.new(val, key: key, children: 2)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
alias_method :insert, :insert_recursive
|
69
|
+
alias_method :[]=, :insert
|
70
|
+
|
71
|
+
def display(node: @root, width: 80)
|
72
|
+
levels = [self.class.display_level(nodes: [node], width: width)]
|
73
|
+
nodes = node.children
|
74
|
+
while nodes.any? { |n| !n.nil? }
|
75
|
+
levels << self.class.display_level(nodes: nodes, width: width)
|
76
|
+
children = []
|
77
|
+
nodes.each { |n|
|
78
|
+
children += n ? n.children : Array.new(@child_slots)
|
79
|
+
}
|
80
|
+
nodes = children
|
81
|
+
end
|
82
|
+
levels.join("\n")
|
83
|
+
end
|
84
|
+
alias_method :to_s, :display
|
85
|
+
end
|
86
|
+
end
|
data/lib/compsci/fibonacci.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
autoload :Matrix, 'matrix'
|
2
2
|
|
3
3
|
module CompSci
|
4
|
-
|
4
|
+
module Fibonacci
|
5
5
|
def self.classic(n)
|
6
6
|
n < 2 ? n : classic(n-1) + classic(n-2)
|
7
7
|
end
|
@@ -17,20 +17,12 @@ module CompSci
|
|
17
17
|
cache[n]
|
18
18
|
end
|
19
19
|
|
20
|
-
# traditional
|
21
20
|
def self.dynamic(n)
|
22
21
|
a, b = 0, 1
|
23
22
|
n.times { a, b = b, a+b }
|
24
23
|
a
|
25
24
|
end
|
26
25
|
|
27
|
-
# fails for n == 0
|
28
|
-
def self.dynamic_fast(n)
|
29
|
-
a, b = 0, 1
|
30
|
-
(n-1).times { a, b = b, a+b }
|
31
|
-
b
|
32
|
-
end
|
33
|
-
|
34
26
|
# https://gist.github.com/havenwood/02cf291b809327d96a3f
|
35
27
|
# slower than dynamic until around n == 500
|
36
28
|
def self.matrix(n)
|