stakach-algorithms 1.0.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.
@@ -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,3 @@
1
+ module Algorithms
2
+ VERSION = "1.0.0"
3
+ end
data/lib/algorithms.rb ADDED
@@ -0,0 +1,69 @@
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
+ module Algorithms; end
53
+ module Containers; end
54
+ end
55
+
56
+ require 'algorithms/search'
57
+ require 'algorithms/sort'
58
+ require 'algorithms/string'
59
+ require 'containers/heap'
60
+ require 'containers/stack'
61
+ require 'containers/deque'
62
+ require 'containers/queue'
63
+ require 'containers/priority_queue'
64
+ require 'containers/rb_tree_map'
65
+ require 'containers/splay_tree_map'
66
+ require 'containers/suffix_array'
67
+ require 'containers/trie'
68
+ require 'containers/kd_tree'
69
+
@@ -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