grosser-algorithms 0.4.0

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