compsci 0.1.1.1 → 0.2.0.1

Sign up to get free protection for your applications and to get access to all the features.
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)