stakach-algorithms 1.0.4 → 1.0.5

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.
@@ -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