algorithms 0.0.1 → 0.1.0

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.
Files changed (54) hide show
  1. data/History.txt +139 -2
  2. data/Manifest +31 -8
  3. data/README +90 -0
  4. data/Rakefile +22 -9
  5. data/algorithms.gemspec +28 -101
  6. data/benchmark.rb +29 -27
  7. data/benchmarks/rbench.rb +16 -0
  8. data/benchmarks/rbench/column.rb +26 -0
  9. data/benchmarks/rbench/group.rb +43 -0
  10. data/benchmarks/rbench/report.rb +53 -0
  11. data/benchmarks/rbench/runner.rb +109 -0
  12. data/benchmarks/rbench/summary.rb +51 -0
  13. data/benchmarks/sorts.rb +33 -0
  14. data/ext/containers/bst/bst.c +205 -0
  15. data/ext/containers/{priority_queue → bst}/extconf.rb +1 -1
  16. data/ext/containers/deque/deque.c +233 -0
  17. data/ext/containers/deque/extconf.rb +4 -0
  18. data/ext/containers/tree_map/extconf.rb +1 -1
  19. data/ext/containers/tree_map/rbtree.c +73 -25
  20. data/lib/algorithms.rb +65 -6
  21. data/lib/algorithms/search.rb +84 -0
  22. data/lib/algorithms/sort.rb +238 -0
  23. data/lib/containers/deque.rb +176 -0
  24. data/lib/containers/heap.rb +451 -111
  25. data/lib/containers/kd_tree.rb +87 -0
  26. data/lib/containers/priority_queue.rb +107 -508
  27. data/lib/containers/queue.rb +62 -23
  28. data/lib/containers/rb_tree_map.rb +398 -0
  29. data/lib/containers/splay_tree_map.rb +274 -0
  30. data/lib/containers/stack.rb +59 -21
  31. data/lib/containers/suffix_array.rb +68 -0
  32. data/lib/containers/trie.rb +182 -0
  33. data/lib/graphs/graph.rb +25 -0
  34. data/spec/bst_spec.rb +31 -0
  35. data/spec/deque_spec.rb +108 -0
  36. data/spec/heap_spec.rb +111 -66
  37. data/spec/kd_tree_spec.rb +89 -0
  38. data/spec/priority_queue_spec.rb +71 -27
  39. data/spec/queue_spec.rb +53 -45
  40. data/spec/rb_tree_map_spec.rb +123 -0
  41. data/spec/search_spec.rb +28 -0
  42. data/spec/sort_spec.rb +28 -0
  43. data/spec/splay_tree_map_spec.rb +102 -0
  44. data/spec/stack_spec.rb +56 -49
  45. data/spec/suffix_array_spec.rb +40 -0
  46. data/spec/trie_spec.rb +59 -0
  47. metadata +54 -32
  48. data/README.txt +0 -58
  49. data/ext/containers/priority_queue/priority_queue.c +0 -948
  50. data/ext/containers/tree_map/Rakefile +0 -4
  51. data/lib/containers/hash.rb +0 -0
  52. data/lib/containers/tree_map.rb +0 -265
  53. data/spec/priority_queue_test.rb +0 -371
  54. data/spec/tree_map_spec.rb +0 -99
@@ -1,146 +1,486 @@
1
- module Containers
2
- # Implemented as a Binomial heap
3
- class Heap
4
- class Node
5
- attr_accessor :object, :left, :right
6
- def initialize(object)
7
- @left = nil
8
- @right = nil
9
- @object = object
10
- end
11
-
12
- end
1
+ =begin rdoc
2
+ A Heap is a container that satisfies the heap property that nodes are always smaller in
3
+ value than their parent node.
13
4
 
14
- attr_reader :root_array
5
+ The Containers::Heap class is flexible and upon initialization, takes an optional block
6
+ that determines how the items are ordered. Two versions that are included are the
7
+ Containers::MaxHeap and Containers::MinHeap that return the largest and smallest items on
8
+ each invocation, respectively.
15
9
 
16
- def initialize(ary=[], &block)
17
- @compare_function = block_given? ? block : lambda { |x, y| (x <=> y) == -1 }
18
- @root_array = []
19
- @size = 0
20
- if !ary.empty?
21
- ary.each { |n| insert(n) }
10
+ This library implements a Fibonacci heap, which allows O(1) complexity for most methods.
11
+ =end
12
+ class Containers::Heap
13
+ include Enumerable
14
+
15
+ # call-seq:
16
+ # size -> int
17
+ #
18
+ # Return the number of elements in the heap.
19
+ def size
20
+ @size
21
+ end
22
+ alias_method :length, :size
23
+
24
+ # call-seq:
25
+ # Heap.new(optional_array) { |x, y| optional_comparison_fn } -> new_heap
26
+ #
27
+ # If an optional array is passed, the entries in the array are inserted into the heap with
28
+ # equal key and value fields. Also, an optional block can be passed to define the function
29
+ # that maintains heap property. For example, a min-heap can be created with:
30
+ #
31
+ # minheap = Heap.new { |x, y| (x <=> y) == -1 }
32
+ # minheap.push(6)
33
+ # minheap.push(10)
34
+ # minheap.pop #=> 6
35
+ #
36
+ # Thus, smaller elements will be parent nodes. The heap defaults to a min-heap if no block
37
+ # is given.
38
+ def initialize(ary=[], &block)
39
+ @compare_fn = block || lambda { |x, y| (x <=> y) == -1 }
40
+ @next = nil
41
+ @size = 0
42
+ @stored = {}
43
+
44
+ ary.each { |n| push(n) } unless ary.empty?
45
+ end
46
+
47
+ # call-seq:
48
+ # push(key, value) -> value
49
+ # push(value) -> value
50
+ #
51
+ # Inserts an item with a given key into the heap. If only one parameter is given,
52
+ # the key is set to the value.
53
+ #
54
+ # Complexity: O(1)
55
+ #
56
+ # heap = MinHeap.new
57
+ # heap.push(1, "Cat")
58
+ # heap.push(2)
59
+ # heap.pop #=> "Cat"
60
+ # heap.pop #=> 2
61
+ def push(key, value=key)
62
+ raise ArgumentError, "Heap keys must not be nil." unless key
63
+ node = Node.new(key, value)
64
+ # Add new node to the left of the @next node
65
+ if @next
66
+ node.right = @next
67
+ node.left = @next.left
68
+ node.left.right = node
69
+ @next.left = node
70
+ if @compare_fn[key, @next.key]
71
+ @next = node
22
72
  end
73
+ else
74
+ @next = node
23
75
  end
76
+ @size += 1
24
77
 
25
- def size
26
- @size
78
+ arr = []
79
+ w = @next.right
80
+ until w == @next do
81
+ arr << w.value
82
+ w = w.right
27
83
  end
84
+ arr << @next.value
85
+ @stored[key] ||= []
86
+ @stored[key] << node
87
+ value
88
+ end
89
+ alias_method :<<, :push
90
+
91
+ # call-seq:
92
+ # has_key?(key) -> true or false
93
+ #
94
+ # Returns true if heap contains the key.
95
+ #
96
+ # Complexity: O(1)
97
+ #
98
+ # minheap = MinHeap.new([1, 2])
99
+ # minheap.has_key?(2) #=> true
100
+ # minheap.has_key?(4) #=> false
101
+ def has_key?(key)
102
+ @stored[key] && !@stored[key].empty? ? true : false
103
+ end
104
+
105
+ # call-seq:
106
+ # next -> value
107
+ # next -> nil
108
+ #
109
+ # Returns the value of the next item in heap order, but does not remove it.
110
+ #
111
+ # Complexity: O(1)
112
+ #
113
+ # minheap = MinHeap.new([1, 2])
114
+ # minheap.next #=> 1
115
+ # minheap.size #=> 2
116
+ def next
117
+ @next && @next.value
118
+ end
119
+
120
+ # call-seq:
121
+ # clear -> nil
122
+ #
123
+ # Removes all elements from the heap, destructively.
124
+ #
125
+ # Complexity: O(1)
126
+ #
127
+ def clear
128
+ @next = nil
129
+ @size = 0
130
+ @stored = {}
131
+ nil
132
+ end
133
+
134
+ # call-seq:
135
+ # empty? -> true or false
136
+ #
137
+ # Returns true if the heap is empty, false otherwise.
138
+ def empty?
139
+ @next.nil?
140
+ end
28
141
 
29
- def peek
30
- return nil if @size < 1
31
- next_index, next_object = -1, nil
142
+ # call-seq:
143
+ # merge!(otherheap) -> merged_heap
144
+ #
145
+ # Does a shallow merge of all the nodes in the other heap.
146
+ #
147
+ # Complexity: O(1)
148
+ #
149
+ # heap = MinHeap.new([5, 6, 7, 8])
150
+ # otherheap = MinHeap.new([1, 2, 3, 4])
151
+ # heap.merge!(otherheap)
152
+ # heap.size #=> 8
153
+ # heap.pop #=> 1
154
+ def merge!(otherheap)
155
+ raise ArgumentError, "Trying to merge a heap with something not a heap" unless otherheap.kind_of? Containers::Heap
156
+ other_root = otherheap.instance_variable_get("@next")
157
+ if other_root
158
+ @stored = @stored.merge(otherheap.instance_variable_get("@stored")) { |key, a, b| (a << b).flatten }
159
+ # Insert othernode's @next node to the left of current @next
160
+ @next.left.right = other_root
161
+ ol = other_root.left
162
+ other_root.left = @next.left
163
+ ol.right = @next
164
+ @next.left = ol
32
165
 
33
- @root_array.size.times do |i|
34
- unless @root_array[i].nil?
35
- if ((next_index == -1) || @compare_function.call(next_object, @root_array[i].object))
36
- next_index, next_object = i, @root_array[i].object
37
- end
38
- end
39
- end
40
- return next_object
166
+ @next = other_root if @compare_fn[other_root.key, @next.key]
41
167
  end
168
+ @size += otherheap.size
169
+ end
42
170
 
43
- def get_next!
44
- return nil if @size < 1
45
- next_index, next_object = -1, nil
171
+ # call-seq:
172
+ # pop -> value
173
+ # pop -> nil
174
+ #
175
+ # Returns the value of the next item in heap order and removes it from the heap.
176
+ #
177
+ # Complexity: O(1)
178
+ #
179
+ # minheap = MinHeap.new([1, 2])
180
+ # minheap.pop #=> 1
181
+ # minheap.size #=> 1
182
+ def pop
183
+ return nil unless @next
184
+ popped = @next
185
+ if @size == 1
186
+ clear
187
+ return popped.value
188
+ end
189
+ # Merge the popped's children into root node
190
+ if @next.child
191
+ @next.child.parent = nil
46
192
 
47
- # Remove the root node containing the maximum from its power-of-2 heap
48
- @root_array.size.times do |i|
49
- unless @root_array[i].nil?
50
- if ((next_index == -1) || @compare_function.call(next_object, @root_array[i].object))
51
- next_index, next_object = i, @root_array[i].object
52
- end
53
- end
193
+ # get rid of parent
194
+ sibling = @next.child.right
195
+ until sibling == @next.child
196
+ sibling.parent = nil
197
+ sibling = sibling.right
54
198
  end
55
199
 
56
- # Temporarily build a binomial queue containing the remaining parts of the power-of-2 heap, and merge this back into the original
57
- temp = []
58
- x = @root_array[next_index].left
59
- (next_index-1).downto(0) do |i|
60
- temp[i] = x
61
- x = x.right
62
- temp[i].right = nil
200
+ # Merge the children into the root. If @next is the only root node, make its child the @next node
201
+ if @next.right == @next
202
+ @next = @next.child
203
+ else
204
+ next_left, next_right = @next.left, @next.right
205
+ current_child = @next.child
206
+ @next.right.left = current_child
207
+ @next.left.right = current_child.right
208
+ current_child.right.left = next_left
209
+ current_child.right = next_right
210
+ @next = @next.right
63
211
  end
64
-
65
- @root_array[next_index] = nil
66
- merge!(temp)
67
- @size -= 1
68
- return next_object
212
+ else
213
+ @next.left.right = @next.right
214
+ @next.right.left = @next.left
215
+ @next = @next.right
69
216
  end
217
+ consolidate
218
+
219
+ unless @stored[popped.key].delete(popped)
220
+ raise "Couldn't delete node from stored nodes hash"
221
+ end
222
+ @size -= 1
223
+
224
+ popped.value
225
+ end
226
+ alias_method :next!, :pop
70
227
 
71
- def insert(object)
72
- c = Node.new(object)
73
- (0..@root_array.size+1).each do |i|
74
- break if c.nil?
75
- if @root_array[i].nil? # The spot is empty, so we use it
76
- @root_array[i] = c
77
- break
228
+ # call-seq:
229
+ # change_key(key, new_key) -> [new_key, value]
230
+ # change_key(key, new_key) -> nil
231
+ #
232
+ # Changes the key from one to another. Doing so must not violate the heap property or
233
+ # an exception will be raised. If the key is found, an array containing the new key and
234
+ # value pair is returned, otherwise nil is returned.
235
+ #
236
+ # In the case of duplicate keys, an arbitrary key is changed. This will be investigated
237
+ # more in the future.
238
+ #
239
+ # Complexity: amortized O(1)
240
+ #
241
+ # minheap = MinHeap.new([1, 2])
242
+ # minheap.change_key(2, 3) #=> raise error since we can't increase the value in a min-heap
243
+ # minheap.change_key(2, 0) #=> [0, 2]
244
+ # minheap.pop #=> 2
245
+ # minheap.pop #=> 1
246
+ def change_key(key, new_key, delete=false)
247
+ return if @stored[key].nil? || @stored[key].empty? || (key == new_key)
248
+
249
+ # Must maintain heap property
250
+ raise "Changing this key would not maintain heap property!" unless (delete || @compare_fn[new_key, key])
251
+ node = @stored[key].shift
252
+ if node
253
+ node.key = new_key
254
+ @stored[new_key] ||= []
255
+ @stored[new_key] << node
256
+ parent = node.parent
257
+ if parent
258
+ # if heap property is violated
259
+ if delete || @compare_fn[new_key, parent.key]
260
+ cut(node, parent)
261
+ cascading_cut(parent)
78
262
  end
79
- c = pair(c, @root_array[i]) # Otherwise, join the two and proceed
80
- @root_array[i] = nil
81
263
  end
82
- @size += 1
83
- return object
264
+ if delete || @compare_fn[node.key, @next.key]
265
+ @next = node
266
+ end
267
+ return [node.key, node.value]
84
268
  end
269
+ nil
270
+ end
85
271
 
86
- def merge!(otherheap)
87
- if (otherheap.class == Containers::Heap || otherheap.class == Containers::MinHeap || otherheap.class == Containers::MaxHeap)
88
- othersize = otherheap.size
89
- otherheap = otherheap.root_array
90
- end
91
- a, b, c = @root_array, otherheap, nil
92
- if(a.size < b.size) # Make sure 'a' is always bigger
93
- a, b = b, a
94
- end
95
-
96
- (0..b.size).each do |i|
97
- case bits(c, b[i], a[i])
98
- when 2 then a[i] = b[i]
99
- when 3 then c = pair(a[i], b[i]); a[i] = nil
100
- when 4 then a[i] = c; c = nil
101
- when 5 then c = pair(c, a[i]); a[i] = nil
102
- when 6..7 then c = pair(c, b[i])
103
- end
104
- end
105
- @root_array = a
106
- @size += othersize if othersize
272
+ # call-seq:
273
+ # delete(key) -> value
274
+ # delete(key) -> nil
275
+ #
276
+ # Deletes the item with associated key and returns it. nil is returned if the key
277
+ # is not found. In the case of nodes with duplicate keys, an arbitrary one is deleted.
278
+ #
279
+ # Complexity: amortized O(log n)
280
+ #
281
+ # minheap = MinHeap.new([1, 2])
282
+ # minheap.delete(1) #=> 1
283
+ # minheap.size #=> 1
284
+ def delete(key)
285
+ pop if change_key(key, nil, true)
286
+ end
287
+
288
+ # Node class used internally
289
+ class Node # :nodoc:
290
+ attr_accessor :parent, :child, :left, :right, :key, :value, :degree, :marked
291
+
292
+ def initialize(key, value)
293
+ @key = key
294
+ @value = value
295
+ @degree = 0
296
+ @marked = false
297
+ @right = self
298
+ @left = self
107
299
  end
108
300
 
109
- private
110
- def bits(c, b, a)
111
- 4*(c.nil? ? 0 : 1) + 2*(b.nil? ? 0 : 1) + (a.nil? ? 0 : 1)
301
+ def marked?
302
+ @marked == true
112
303
  end
113
304
 
114
- def pair(p, q)
115
- if @compare_function.call(p.object, q.object)
116
- p.right = q.left
117
- q.left = p
118
- return q
119
- else
120
- q.right = p.left
121
- p.left = q
122
- return p
123
- end
305
+ end
306
+
307
+ # make node a child of a parent node
308
+ def link_nodes(child, parent)
309
+ # link the child's siblings
310
+ child.left.right = child.right
311
+ child.right.left = child.left
312
+
313
+ child.parent = parent
314
+
315
+ # if parent doesn't have children, make new child its only child
316
+ if parent.child.nil?
317
+ parent.child = child.right = child.left = child
318
+ else # otherwise insert new child into parent's children list
319
+ current_child = parent.child
320
+ child.left = current_child
321
+ child.right = current_child.right
322
+ current_child.right.left = child
323
+ current_child.right = child
124
324
  end
325
+ parent.degree += 1
326
+ child.marked = false
125
327
  end
328
+ private :link_nodes
126
329
 
127
- class MaxHeap < Heap
128
- def initialize(ary=[])
129
- super(ary) { |x, y| (x <=> y) == -1 }
330
+ # Makes sure the structure does not contain nodes in the root list with equal degrees
331
+ def consolidate
332
+ roots = []
333
+ root = @next
334
+ min = root
335
+ # find the nodes in the list
336
+ loop do
337
+ roots << root
338
+ root = root.right
339
+ break if root == @next
130
340
  end
131
-
132
- def get_max!
133
- get_next!
341
+ degrees = []
342
+ roots.each do |root|
343
+ min = root if @compare_fn[root.key, min.key]
344
+ # check if we need to merge
345
+ if degrees[root.degree].nil? # no other node with the same degree
346
+ degrees[root.degree] = root
347
+ next
348
+ else # there is another node with the same degree, consolidate them
349
+ degree = root.degree
350
+ until degrees[degree].nil? do
351
+ other_root_with_degree = degrees[degree]
352
+ if @compare_fn[root.key, other_root_with_degree.key] # determine which node is the parent, which one is the child
353
+ smaller, larger = root, other_root_with_degree
354
+ else
355
+ smaller, larger = other_root_with_degree, root
356
+ end
357
+ link_nodes(larger, smaller)
358
+ degrees[degree] = nil
359
+ root = smaller
360
+ degree += 1
361
+ end
362
+ degrees[degree] = root
363
+ min = root if min.key == root.key # this fixes a bug with duplicate keys not being in the right order
364
+ end
134
365
  end
366
+ @next = min
135
367
  end
368
+ private :consolidate
136
369
 
137
- class MinHeap < Heap
138
- def initialize(ary=[])
139
- super(ary) { |x, y| (x <=> y) == 1 }
370
+ def cascading_cut(node)
371
+ p = node.parent
372
+ if p
373
+ if node.marked?
374
+ cut(node, p)
375
+ cascading_cut(p)
376
+ else
377
+ node.marked = true
378
+ end
140
379
  end
141
-
142
- def get_min!
143
- get_next!
380
+ end
381
+ private :cascading_cut
382
+
383
+ # remove x from y's children and add x to the root list
384
+ def cut(x, y)
385
+ x.left.right = x.right
386
+ x.right.left = x.left
387
+ y.degree -= 1
388
+ if (y.degree == 0)
389
+ y.child = nil
390
+ elsif (y.child == x)
391
+ y.child = x.right
144
392
  end
393
+ x.right = @next
394
+ x.left = @next.left
395
+ @next.left = x
396
+ x.left.right = x
397
+ x.parent = nil
398
+ x.marked = false
399
+ end
400
+ private :cut
401
+
402
+ end
403
+
404
+ # A MaxHeap is a heap where the items are returned in descending order of key value.
405
+ class Containers::MaxHeap < Containers::Heap
406
+
407
+ # call-seq:
408
+ # MaxHeap.new(ary) -> new_heap
409
+ #
410
+ # Creates a new MaxHeap with an optional array parameter of items to insert into the heap.
411
+ # A MaxHeap is created by calling Heap.new { |x, y| (x <=> y) == 1 }, so this is a convenience class.
412
+ #
413
+ # maxheap = MaxHeap.new([1, 2, 3, 4])
414
+ # maxheap.pop #=> 4
415
+ # maxheap.pop #=> 3
416
+ def initialize(ary=[])
417
+ super(ary) { |x, y| (x <=> y) == 1 }
418
+ end
419
+
420
+ # call-seq:
421
+ # max -> value
422
+ # max -> nil
423
+ #
424
+ # Returns the item with the largest key, but does not remove it from the heap.
425
+ #
426
+ # maxheap = MaxHeap.new([1, 2, 3, 4])
427
+ # maxheap.max #=> 4
428
+ def max
429
+ self.next
430
+ end
431
+
432
+ # call-seq:
433
+ # max! -> value
434
+ # max! -> nil
435
+ #
436
+ # Returns the item with the largest key and removes it from the heap.
437
+ #
438
+ # maxheap = MaxHeap.new([1, 2, 3, 4])
439
+ # maxheap.max! #=> 4
440
+ # maxheap.size #=> 3
441
+ def max!
442
+ self.pop
443
+ end
444
+ end
445
+
446
+ # A MinHeap is a heap where the items are returned in ascending order of key value.
447
+ class Containers::MinHeap < Containers::Heap
448
+
449
+ # call-seq:
450
+ # MinHeap.new(ary) -> new_heap
451
+ #
452
+ # Creates a new MinHeap with an optional array parameter of items to insert into the heap.
453
+ # A MinHeap is created by calling Heap.new { |x, y| (x <=> y) == -1 }, so this is a convenience class.
454
+ #
455
+ # minheap = MinHeap.new([1, 2, 3, 4])
456
+ # minheap.pop #=> 1
457
+ # minheap.pop #=> 2
458
+ def initialize(ary=[])
459
+ super(ary) { |x, y| (x <=> y) == -1 }
460
+ end
461
+
462
+ # call-seq:
463
+ # min -> value
464
+ # min -> nil
465
+ #
466
+ # Returns the item with the smallest key, but does not remove it from the heap.
467
+ #
468
+ # minheap = MinHeap.new([1, 2, 3, 4])
469
+ # minheap.min #=> 1
470
+ def min
471
+ self.next
472
+ end
473
+
474
+ # call-seq:
475
+ # min! -> value
476
+ # min! -> nil
477
+ #
478
+ # Returns the item with the smallest key and removes it from the heap.
479
+ #
480
+ # minheap = MinHeap.new([1, 2, 3, 4])
481
+ # minheap.min! #=> 1
482
+ # minheap.size #=> 3
483
+ def min!
484
+ self.pop
145
485
  end
146
486
  end