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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e4f7afa18fdbd091cf4fed0668fe59204f8f05fa
4
- data.tar.gz: c123e0795298ac870f4b769f97818144e50a6f74
3
+ metadata.gz: 3fa74f40bbc97fa685c811d2b15738da31d69c66
4
+ data.tar.gz: '02965023a412fae230e7fe3e42704982c6410bab'
5
5
  SHA512:
6
- metadata.gz: 9072889f4e3404d945686c1dccf2818d3fd359354f7b78fa42562ecb380918abc9899cacf14ce7e1e4c29b31542b1ba0c57b8f1f15b058b12132302fc77f8f99
7
- data.tar.gz: adb4c6ec9b7b23ebc5781204db5fb1062826ee19426f8c7e3ad1098c362c535cdc0e5b71c60abe9b009ed66f4ef317b3bffa36c4f8fdbda84d37fac3555f6788
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
- * `ChildNode` adds
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 example](examples/heap.rb) which can be executed (among other examples)
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 around 500k pushes per second, constant up past 1M
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 = 800
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.1.1
1
+ 0.2.0.1
@@ -22,4 +22,5 @@ Gem::Specification.new do |s|
22
22
  s.add_development_dependency "flay", "~> 0"
23
23
  s.add_development_dependency "roodi", "~> 0"
24
24
  s.add_development_dependency "ruby-prof", "~> 0"
25
+ s.add_development_dependency "benchmark-ips", "~> 2.0"
25
26
  end
@@ -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
@@ -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
@@ -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(ChildNode, my_vals.shift)
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 ChildNode, rand(99)
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 ChildNode, vals.shift
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
@@ -1,7 +1,7 @@
1
1
  autoload :Matrix, 'matrix'
2
2
 
3
3
  module CompSci
4
- class Fibonacci
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)