algorithms 0.3.0-jruby

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 (44) hide show
  1. data/History.txt +172 -0
  2. data/Manifest +43 -0
  3. data/README.markdown +93 -0
  4. data/Rakefile +31 -0
  5. data/algorithms.gemspec +33 -0
  6. data/benchmarks/deque.rb +17 -0
  7. data/benchmarks/sorts.rb +34 -0
  8. data/benchmarks/treemaps.rb +51 -0
  9. data/ext/containers/deque/deque.c +247 -0
  10. data/ext/containers/deque/extconf.rb +4 -0
  11. data/ext/containers/rbtree_map/extconf.rb +4 -0
  12. data/ext/containers/rbtree_map/rbtree.c +498 -0
  13. data/ext/containers/splaytree_map/extconf.rb +4 -0
  14. data/ext/containers/splaytree_map/splaytree.c +419 -0
  15. data/lib/algorithms.rb +68 -0
  16. data/lib/algorithms/search.rb +84 -0
  17. data/lib/algorithms/sort.rb +238 -0
  18. data/lib/containers/deque.rb +171 -0
  19. data/lib/containers/heap.rb +486 -0
  20. data/lib/containers/kd_tree.rb +110 -0
  21. data/lib/containers/priority_queue.rb +113 -0
  22. data/lib/containers/queue.rb +68 -0
  23. data/lib/containers/rb_tree_map.rb +398 -0
  24. data/lib/containers/splay_tree_map.rb +269 -0
  25. data/lib/containers/stack.rb +67 -0
  26. data/lib/containers/suffix_array.rb +68 -0
  27. data/lib/containers/trie.rb +182 -0
  28. data/spec/deque_gc_mark_spec.rb +18 -0
  29. data/spec/deque_spec.rb +108 -0
  30. data/spec/heap_spec.rb +126 -0
  31. data/spec/kd_expected_out.txt +10000 -0
  32. data/spec/kd_test_in.txt +10000 -0
  33. data/spec/kd_tree_spec.rb +34 -0
  34. data/spec/map_gc_mark_spec.rb +27 -0
  35. data/spec/priority_queue_spec.rb +75 -0
  36. data/spec/queue_spec.rb +61 -0
  37. data/spec/rb_tree_map_spec.rb +123 -0
  38. data/spec/search_spec.rb +28 -0
  39. data/spec/sort_spec.rb +28 -0
  40. data/spec/splay_tree_map_spec.rb +106 -0
  41. data/spec/stack_spec.rb +60 -0
  42. data/spec/suffix_array_spec.rb +40 -0
  43. data/spec/trie_spec.rb +59 -0
  44. metadata +122 -0
@@ -0,0 +1,84 @@
1
+ =begin rdoc
2
+ This module implements search algorithms. Documentation is provided for each algorithm.
3
+
4
+ =end
5
+ module Algorithms::Search
6
+ # Binary Search: This search finds an item in log(n) time provided that the container is already sorted.
7
+ # The method returns the item if it is found, or nil if it is not. If there are duplicates, the first one
8
+ # found is returned, and this is not guaranteed to be the smallest or largest item.
9
+ #
10
+ # Complexity: O(lg N)
11
+ #
12
+ # Algorithms::Search.binary_search([1, 2, 3], 1) #=> 1
13
+ # Algorithms::Search.binary_search([1, 2, 3], 4) #=> nil
14
+ def self.binary_search(container, item)
15
+ return nil if item.nil?
16
+ low = 0
17
+ high = container.size - 1
18
+ while low <= high
19
+ mid = (low + high) / 2
20
+ val = container[mid]
21
+ if val > item
22
+ high = mid - 1
23
+ elsif val < item
24
+ low = mid + 1
25
+ else
26
+ return val
27
+ end
28
+ end
29
+ nil
30
+ end
31
+
32
+ # Knuth-Morris-Pratt Algorithm substring search algorithm: Efficiently finds the starting position of a
33
+ # substring in a string. The algorithm calculates the best position to resume searching from if a failure
34
+ # occurs.
35
+ #
36
+ # The method returns the index of the starting position in the string where the substring is found. If there
37
+ # is no match, nil is returned.
38
+ #
39
+ # Complexity: O(n + k), where n is the length of the string and k is the length of the substring.
40
+ #
41
+ # Algorithms::Search.kmp_search("ABC ABCDAB ABCDABCDABDE", "ABCDABD") #=> 15
42
+ # Algorithms::Search.kmp_search("ABC ABCDAB ABCDABCDABDE", "ABCDEF") #=> nil
43
+ def self.kmp_search(string, substring)
44
+ return nil if string.nil? or substring.nil?
45
+
46
+ # create failure function table
47
+ pos = 2
48
+ cnd = 0
49
+ failure_table = [-1, 0]
50
+ while pos < substring.length
51
+ if substring[pos - 1] == substring[cnd]
52
+ failure_table[pos] = cnd + 1
53
+ pos += 1
54
+ cnd += 1
55
+ elsif cnd > 0
56
+ cnd = failure_table[cnd]
57
+ else
58
+ failure_table[pos] = 0
59
+ pos += 1
60
+ end
61
+ end
62
+
63
+ m = i = 0
64
+ while m + i < string.length
65
+ if substring[i] == string[m + i]
66
+ i += 1
67
+ return m if i == substring.length
68
+ else
69
+ m = m + i - failure_table[i]
70
+ i = failure_table[i] if i > 0
71
+ end
72
+ end
73
+ return nil
74
+ end
75
+
76
+ # Allows kmp_search to be called as an instance method in classes that include the Search module.
77
+ #
78
+ # class String; include Algorithms::Search; end
79
+ # "ABC ABCDAB ABCDABCDABDE".kmp_search("ABCDABD") #=> 15
80
+ def kmp_search(substring)
81
+ Algorithms::Search.kmp_search(self, substring)
82
+ end
83
+
84
+ end
@@ -0,0 +1,238 @@
1
+ require 'containers/heap' # for heapsort
2
+
3
+ =begin rdoc
4
+ This module implements sorting algorithms. Documentation is provided for each algorithm.
5
+
6
+ =end
7
+ module Algorithms::Sort
8
+ # Bubble sort: A very naive sort that keeps swapping elements until the container is sorted.
9
+ # Requirements: Needs to be able to compare elements with <=>, and the [] []= methods should
10
+ # be implemented for the container.
11
+ # Time Complexity: О(n^2)
12
+ # Space Complexity: О(n) total, O(1) auxiliary
13
+ # Stable: Yes
14
+ #
15
+ # Algorithms::Sort.bubble_sort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
16
+ def self.bubble_sort(container)
17
+ loop do
18
+ swapped = false
19
+ (container.size-1).times do |i|
20
+ if (container[i] <=> container[i+1]) == 1
21
+ container[i], container[i+1] = container[i+1], container[i] # Swap
22
+ swapped = true
23
+ end
24
+ end
25
+ break unless swapped
26
+ end
27
+ container
28
+ end
29
+
30
+ # Comb sort: A variation on bubble sort that dramatically improves performance.
31
+ # Source: http://yagni.com/combsort/
32
+ # Requirements: Needs to be able to compare elements with <=>, and the [] []= methods should
33
+ # be implemented for the container.
34
+ # Time Complexity: О(n^2)
35
+ # Space Complexity: О(n) total, O(1) auxiliary
36
+ # Stable: Yes
37
+ #
38
+ # Algorithms::Sort.comb_sort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
39
+ def self.comb_sort(container)
40
+ container
41
+ gap = container.size
42
+ loop do
43
+ gap = gap * 10/13
44
+ gap = 11 if gap == 9 || gap == 10
45
+ gap = 1 if gap < 1
46
+ swapped = false
47
+ (container.size - gap).times do |i|
48
+ if (container[i] <=> container[i + gap]) == 1
49
+ container[i], container[i+gap] = container[i+gap], container[i] # Swap
50
+ swapped = true
51
+ end
52
+ end
53
+ break if !swapped && gap == 1
54
+ end
55
+ container
56
+ end
57
+
58
+ # Selection sort: A naive sort that goes through the container and selects the smallest element,
59
+ # putting it at the beginning. Repeat until the end is reached.
60
+ # Requirements: Needs to be able to compare elements with <=>, and the [] []= methods should
61
+ # be implemented for the container.
62
+ # Time Complexity: О(n^2)
63
+ # Space Complexity: О(n) total, O(1) auxiliary
64
+ # Stable: Yes
65
+ #
66
+ # Algorithms::Sort.selection_sort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
67
+ def self.selection_sort(container)
68
+ 0.upto(container.size-1) do |i|
69
+ min = i
70
+ (i+1).upto(container.size-1) do |j|
71
+ min = j if (container[j] <=> container[min]) == -1
72
+ end
73
+ container[i], container[min] = container[min], container[i] # Swap
74
+ end
75
+ container
76
+ end
77
+
78
+ # Heap sort: Uses a heap (implemented by the Containers module) to sort the collection.
79
+ # Requirements: Needs to be able to compare elements with <=>
80
+ # Time Complexity: О(n^2)
81
+ # Space Complexity: О(n) total, O(1) auxiliary
82
+ # Stable: Yes
83
+ #
84
+ # Algorithms::Sort.heapsort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
85
+ def self.heapsort(container)
86
+ heap = Containers::Heap.new(container)
87
+ ary = []
88
+ ary << heap.pop until heap.empty?
89
+ ary
90
+ end
91
+
92
+ # Insertion sort: Elements are inserted sequentially into the right position.
93
+ # Requirements: Needs to be able to compare elements with <=>, and the [] []= methods should
94
+ # be implemented for the container.
95
+ # Time Complexity: О(n^2)
96
+ # Space Complexity: О(n) total, O(1) auxiliary
97
+ # Stable: Yes
98
+ #
99
+ # Algorithms::Sort.insertion_sort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
100
+ def self.insertion_sort(container)
101
+ return container if container.size < 2
102
+ (1..container.size-1).each do |i|
103
+ value = container[i]
104
+ j = i-1
105
+ while j >= 0 and container[j] > value do
106
+ container[j+1] = container[j]
107
+ j = j-1
108
+ end
109
+ container[j+1] = value
110
+ end
111
+ container
112
+ end
113
+
114
+ # Shell sort: Similar approach as insertion sort but slightly better.
115
+ # Requirements: Needs to be able to compare elements with <=>, and the [] []= methods should
116
+ # be implemented for the container.
117
+ # Time Complexity: О(n^2)
118
+ # Space Complexity: О(n) total, O(1) auxiliary
119
+ # Stable: Yes
120
+ #
121
+ # Algorithms::Sort.shell_sort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
122
+ def self.shell_sort(container)
123
+ increment = container.size/2
124
+ while increment > 0 do
125
+ (increment..container.size-1).each do |i|
126
+ temp = container[i]
127
+ j = i
128
+ while j >= increment && container[j - increment] > temp do
129
+ container[j] = container[j-increment]
130
+ j -= increment
131
+ end
132
+ container[j] = temp
133
+ end
134
+ increment = (increment == 2 ? 1 : (increment / 2.2).round)
135
+ end
136
+ container
137
+ end
138
+
139
+ # Quicksort: A divide-and-conquer sort that recursively partitions a container until it is sorted.
140
+ # Requirements: Container should implement #pop and include the Enumerable module.
141
+ # Time Complexity: О(n log n) average, O(n^2) worst-case
142
+ # Space Complexity: О(n) auxiliary
143
+ # Stable: No
144
+ #
145
+ # Algorithms::Sort.quicksort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
146
+ # def self.quicksort(container)
147
+ # return [] if container.empty?
148
+ #
149
+ # x, *xs = container
150
+ #
151
+ # quicksort(xs.select { |i| i < x }) + [x] + quicksort(xs.select { |i| i >= x })
152
+ # end
153
+
154
+ def self.partition(data, left, right)
155
+ pivot = data[front]
156
+ left += 1
157
+
158
+ while left <= right do
159
+ if data[frontUnknown] < pivot
160
+ back += 1
161
+ data[frontUnknown], data[back] = data[back], data[frontUnknown] # Swap
162
+ end
163
+
164
+ frontUnknown += 1
165
+ end
166
+
167
+ data[front], data[back] = data[back], data[front] # Swap
168
+ back
169
+ end
170
+
171
+
172
+ # def self.quicksort(container, left = 0, right = container.size - 1)
173
+ # if left < right
174
+ # middle = partition(container, left, right)
175
+ # quicksort(container, left, middle - 1)
176
+ # quicksort(container, middle + 1, right)
177
+ # end
178
+ # end
179
+
180
+ def self.quicksort(container)
181
+ bottom, top = [], []
182
+ top[0] = 0
183
+ bottom[0] = container.size
184
+ i = 0
185
+ while i >= 0 do
186
+ l = top[i]
187
+ r = bottom[i] - 1;
188
+ if l < r
189
+ pivot = container[l]
190
+ while l < r do
191
+ r -= 1 while (container[r] >= pivot && l < r)
192
+ if (l < r)
193
+ container[l] = container[r]
194
+ l += 1
195
+ end
196
+ l += 1 while (container[l] <= pivot && l < r)
197
+ if (l < r)
198
+ container[r] = container[l]
199
+ r -= 1
200
+ end
201
+ end
202
+ container[l] = pivot
203
+ top[i+1] = l + 1
204
+ bottom[i+1] = bottom[i]
205
+ bottom[i] = l
206
+ i += 1
207
+ else
208
+ i -= 1
209
+ end
210
+ end
211
+ container
212
+ end
213
+
214
+ # Mergesort: A stable divide-and-conquer sort that sorts small chunks of the container and then merges them together.
215
+ # Returns an array of the sorted elements.
216
+ # Requirements: Container should implement []
217
+ # Time Complexity: О(n log n) average and worst-case
218
+ # Space Complexity: О(n) auxiliary
219
+ # Stable: Yes
220
+ #
221
+ # Algorithms::Sort.mergesort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
222
+ def self.mergesort(container)
223
+ return container if container.size <= 1
224
+ mid = container.size / 2
225
+ left = container[0...mid]
226
+ right = container[mid...container.size]
227
+ merge(mergesort(left), mergesort(right))
228
+ end
229
+
230
+ def self.merge(left, right)
231
+ sorted = []
232
+ until left.empty? or right.empty?
233
+ left.first <= right.first ? sorted << left.shift : sorted << right.shift
234
+ end
235
+ sorted + left + right
236
+ end
237
+
238
+ end
@@ -0,0 +1,171 @@
1
+ =begin rdoc
2
+ A Deque is a container that allows items to be added and removed from both the front and back,
3
+ acting as a combination of a Stack and Queue.
4
+
5
+ This implementation uses a doubly-linked list, guaranteeing O(1) complexity for all operations.
6
+ =end
7
+ class Containers::RubyDeque
8
+ include Enumerable
9
+
10
+ Node = Struct.new(:left, :right, :obj)
11
+
12
+ # Create a new Deque. Takes an optional array argument to initialize the Deque.
13
+ #
14
+ # d = Containers::Deque.new([1, 2, 3])
15
+ # d.front #=> 1
16
+ # d.back #=> 3
17
+ def initialize(ary=[])
18
+ @front = nil
19
+ @back = nil
20
+ @size = 0
21
+ ary.to_a.each { |obj| push_back(obj) }
22
+ end
23
+
24
+ # Returns true if the Deque is empty, false otherwise.
25
+ def empty?
26
+ @size == 0
27
+ end
28
+
29
+ # Removes all the objects in the Deque.
30
+ def clear
31
+ @front = @back = nil
32
+ @size = 0
33
+ end
34
+
35
+ # Return the number of items in the Deque.
36
+ #
37
+ # d = Containers::Deque.new([1, 2, 3])
38
+ # d.size #=> 3
39
+ def size
40
+ @size
41
+ end
42
+ alias_method :length, :size
43
+
44
+ # Returns the object at the front of the Deque but does not remove it.
45
+ #
46
+ # d = Containers::Deque.new
47
+ # d.push_front(1)
48
+ # d.push_front(2)
49
+ # d.front #=> 2
50
+ def front
51
+ @front && @front.obj
52
+ end
53
+
54
+ # Returns the object at the back of the Deque but does not remove it.
55
+ #
56
+ # d = Containers::Deque.new
57
+ # d.push_front(1)
58
+ # d.push_front(2)
59
+ # d.back #=> 1
60
+ def back
61
+ @back && @back.obj
62
+ end
63
+
64
+ # Adds an object at the front of the Deque.
65
+ #
66
+ # d = Containers::Deque.new([1, 2, 3])
67
+ # d.push_front(0)
68
+ # d.pop_front #=> 0
69
+ def push_front(obj)
70
+ node = Node.new(nil, nil, obj)
71
+ if @front
72
+ node.right = @front
73
+ @front.left = node
74
+ @front = node
75
+ else
76
+ @front = @back = node
77
+ end
78
+ @size += 1
79
+ obj
80
+ end
81
+
82
+ # Adds an object at the back of the Deque.
83
+ #
84
+ # d = Containers::Deque.new([1, 2, 3])
85
+ # d.push_back(4)
86
+ # d.pop_back #=> 4
87
+ def push_back(obj)
88
+ node = Node.new(nil, nil, obj)
89
+ if @back
90
+ node.left = @back
91
+ @back.right = node
92
+ @back = node
93
+ else
94
+ @front = @back = node
95
+ end
96
+ @size += 1
97
+ obj
98
+ end
99
+
100
+ # Returns the object at the front of the Deque and removes it.
101
+ #
102
+ # d = Containers::Deque.new
103
+ # d.push_front(1)
104
+ # d.push_front(2)
105
+ # d.pop_front #=> 2
106
+ # d.size #=> 1
107
+ def pop_front
108
+ return nil unless @front
109
+ node = @front
110
+ if @size == 1
111
+ clear
112
+ return node.obj
113
+ else
114
+ @front.right.left = nil
115
+ @front = @front.right
116
+ end
117
+ @size -= 1
118
+ node.obj
119
+ end
120
+
121
+ # Returns the object at the back of the Deque and removes it.
122
+ #
123
+ # d = Containers::Deque.new
124
+ # d.push_front(1)
125
+ # d.push_front(2)
126
+ # d.pop_back #=> 1
127
+ # d.size #=> 1
128
+ def pop_back
129
+ return nil unless @back
130
+ node = @back
131
+ if @size == 1
132
+ clear
133
+ return node.obj
134
+ else
135
+ @back.left.right = nil
136
+ @back = @back.left
137
+ end
138
+ @size -= 1
139
+ node.obj
140
+ end
141
+
142
+ # Iterate over the Deque in FIFO order.
143
+ def each_forward
144
+ return unless @front
145
+ node = @front
146
+ while node
147
+ yield node.obj
148
+ node = node.right
149
+ end
150
+ end
151
+ alias_method :each, :each_forward
152
+
153
+ # Iterate over the Deque in LIFO order.
154
+ def each_backward
155
+ return unless @back
156
+ node = @back
157
+ while node
158
+ yield node.obj
159
+ node = node.left
160
+ end
161
+ end
162
+ alias_method :reverse_each, :each_backward
163
+
164
+ end
165
+
166
+ begin
167
+ require 'CDeque'
168
+ Containers::Deque = Containers::CDeque
169
+ rescue LoadError # C Version could not be found, try ruby version
170
+ Containers::Deque = Containers::RubyDeque
171
+ end