stakach-algorithms 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,86 +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
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
86
  end
@@ -1,243 +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
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
243
  end