algorithms 0.6.1 → 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7cee1c06e078dc124212a3462d765d26ce5fbe16dd6b31ca6392388d8f51644c
4
+ data.tar.gz: 491e3540ee5567752d208bb7b8502d1ee998b55e9664c66b5cf5fc17394e10e7
5
+ SHA512:
6
+ metadata.gz: a4a533a2053ae799f87df22b51ff93744e8128e0982239f3d6717bfb6f8ee8817d9c6ebffd6b4a2b478694a3f2f742fc4465eba242e5d2d0d23bfc0c881c1daa
7
+ data.tar.gz: 4ffaed30fc92fca18dd258102a702f8fceb1a77594ac6b086c627e1a3ab8ae1ad6650295acd0fb423a6ce441b6b7ab686bb56ae9ff937448d031f319d937a427
data/README.markdown CHANGED
@@ -1,5 +1,7 @@
1
1
  # algorithms [![Build Status](https://travis-ci.org/kanwei/algorithms.png)](https://travis-ci.org/kanwei/algorithms)
2
2
 
3
+ [API Documentation](http://kanwei.github.io/algorithms/)
4
+
3
5
  ## DESCRIPTION:
4
6
 
5
7
  Started as a [Google Summer of Code 2008](http://code.google.com/soc/2008/ruby/about.html) project
@@ -41,6 +43,7 @@ compare performance in different situations.
41
43
  - Shell sort Algorithms::Sort.shell_sort
42
44
  - Quicksort Algorithms::Sort.quicksort
43
45
  - Mergesort Algorithms::Sort.mergesort
46
+ - Dual-Pivot Quicksort Algorithms::Sort.dualpivotquicksort
44
47
 
45
48
  ## SYNOPSIS:
46
49
 
@@ -60,25 +63,4 @@ compare performance in different situations.
60
63
 
61
64
  ## LICENSE:
62
65
 
63
- (The MIT License)
64
-
65
- Ruby Algorithms and Containers project is Copyright (c) 2009 Kanwei Li
66
-
67
- Permission is hereby granted, free of charge, to any person obtaining
68
- a copy of this software and associated documentation files (the
69
- 'Software'), to deal in the Software without restriction, including
70
- without limitation the rights to use, copy, modify, merge, publish,
71
- distribute, sublicense, and/or sell copies of the Software, and to
72
- permit persons to whom the Software is furnished to do so, subject to
73
- the following conditions:
74
-
75
- The above copyright notice and this permission notice shall be
76
- included in all copies or substantial portions of the Software.
77
-
78
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
79
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
80
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
81
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
82
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
83
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
84
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
66
+ See [LICENSE.md](LICENSE.md).
data/Rakefile CHANGED
@@ -16,3 +16,7 @@ if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
16
16
  else
17
17
  task :default => [:compile, :spec]
18
18
  end
19
+
20
+ task :rdoc do
21
+ `rdoc -f hanna --main algorithms.rb -t "Ruby Algorithms and Containers Documentation"`
22
+ end
data/algorithms.gemspec CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "algorithms"
5
- s.version = "0.6.1"
5
+ s.version = "1.0.0"
6
6
 
7
7
  s.authors = ["Kanwei Li"]
8
8
  s.email = "kanwei@gmail.com"
9
9
  s.license = 'MIT'
10
- s.date = "2013-01-22"
10
+ s.date = "2021-04-04"
11
11
  s.summary = "Useful algorithms and data structures for Ruby. Optional C extensions."
12
12
  s.description = "Heap, Priority Queue, Deque, Stack, Queue, Red-Black Trees, Splay Trees, sorting algorithms, and more"
13
13
  if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
@@ -19,5 +19,4 @@ Gem::Specification.new do |s|
19
19
  s.homepage = "https://github.com/kanwei/algorithms"
20
20
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Algorithms", "--main", "README.markdown"]
21
21
  s.require_paths = ["lib", "ext"]
22
- s.rubyforge_project = "algorithms"
23
22
  end
@@ -26,7 +26,7 @@ long levenshtein_distance(VALUE str1, VALUE str2) {
26
26
  s1_len++;
27
27
  s2_len++;
28
28
 
29
- d = malloc(sizeof(typeof(d)) * (s1_len) * (s2_len));
29
+ d = xmalloc(sizeof(typeof(d)) * s1_len * s2_len);
30
30
 
31
31
  for (i = 0; i < s1_len; i++) {
32
32
  d[i] = i; // d[i, 0] = i
@@ -49,7 +49,7 @@ long levenshtein_distance(VALUE str1, VALUE str2) {
49
49
  }
50
50
  }
51
51
  i = d[s1_len * s2_len -1];
52
- free(d);
52
+ xfree(d);
53
53
  return i;
54
54
  }
55
55
 
@@ -172,7 +172,7 @@ static void recursively_free_nodes(bst_node *node) {
172
172
  if(node) {
173
173
  recursively_free_nodes(node->left);
174
174
  recursively_free_nodes(node->right);
175
- free(node);
175
+ xfree(node);
176
176
  }
177
177
  }
178
178
 
@@ -19,7 +19,7 @@ void free_nodes(deque_node *node) {
19
19
  deque_node *next;
20
20
  while(node) {
21
21
  next = node->right;
22
- free(node);
22
+ xfree(node);
23
23
  node = next;
24
24
  }
25
25
  return;
@@ -71,7 +71,7 @@ static void deque_free(void *ptr) {
71
71
  if (ptr) {
72
72
  deque *deque = ptr;
73
73
  free_nodes(deque->front);
74
- free(deque);
74
+ xfree(deque);
75
75
  }
76
76
  }
77
77
 
@@ -31,7 +31,7 @@ static void recursively_free_nodes(rbtree_node *node) {
31
31
  if(node) {
32
32
  recursively_free_nodes(node->left);
33
33
  recursively_free_nodes(node->right);
34
- free(node);
34
+ xfree(node);
35
35
  }
36
36
  return;
37
37
  }
@@ -198,7 +198,7 @@ static rbtree_node* delete_min(rbtree_node *h, VALUE *deleted_value) {
198
198
  if ( !h->left ) {
199
199
  if(deleted_value)
200
200
  *deleted_value = h->value;
201
- free(h);
201
+ xfree(h);
202
202
  return NULL;
203
203
  }
204
204
 
@@ -216,7 +216,7 @@ static rbtree_node* delete_max(rbtree_node *h, VALUE *deleted_value) {
216
216
 
217
217
  if ( !h->right ) {
218
218
  *deleted_value = h->value;
219
- free(h);
219
+ xfree(h);
220
220
  return NULL;
221
221
  }
222
222
 
@@ -245,7 +245,7 @@ static rbtree_node* delete(rbtree *tree, rbtree_node *node, VALUE key, VALUE *de
245
245
  cmp = tree->compare_function(key, node->key);
246
246
  if ( (cmp == 0) && !node->right ) {
247
247
  *deleted_value = node->value;
248
- free(node);
248
+ xfree(node);
249
249
  return NULL;
250
250
  }
251
251
 
@@ -339,7 +339,7 @@ static void rbtree_mark(void *ptr) {
339
339
  }
340
340
  old = current;
341
341
  current = current->next;
342
- free(old);
342
+ xfree(old);
343
343
  }
344
344
  }
345
345
  }
@@ -349,7 +349,7 @@ static void rbtree_free(void *ptr) {
349
349
  if (ptr) {
350
350
  rbtree *tree = ptr;
351
351
  recursively_free_nodes(tree->root);
352
- free(tree);
352
+ xfree(tree);
353
353
  }
354
354
  }
355
355
 
@@ -28,7 +28,7 @@ static void recursively_free_nodes(splaytree_node *node) {
28
28
  if(node) {
29
29
  recursively_free_nodes(node->left);
30
30
  recursively_free_nodes(node->right);
31
- free(node);
31
+ xfree(node);
32
32
  }
33
33
  return;
34
34
  }
@@ -199,7 +199,7 @@ static splaytree_node* delete(splaytree *tree, splaytree_node *n, VALUE key, VAL
199
199
  x = splay(tree, n->left, key);
200
200
  x->right = n->right;
201
201
  }
202
- free(n);
202
+ xfree(n);
203
203
  if (x) {
204
204
  x->size = tsize-1;
205
205
  }
@@ -282,7 +282,7 @@ static void splaytree_mark(void *ptr) {
282
282
  }
283
283
  old = current;
284
284
  current = current->next;
285
- free(old);
285
+ xfree(old);
286
286
  }
287
287
  }
288
288
  }
@@ -292,7 +292,7 @@ static void splaytree_free(void *ptr) {
292
292
  if (ptr) {
293
293
  splaytree *tree = ptr;
294
294
  recursively_free_nodes(tree->root);
295
- free(tree);
295
+ xfree(tree);
296
296
  }
297
297
  }
298
298
 
data/lib/algorithms.rb CHANGED
@@ -43,6 +43,7 @@
43
43
  - Shell sort - Algorithms::Sort.shell_sort
44
44
  - Quicksort - Algorithms::Sort.quicksort
45
45
  - Mergesort - Algorithms::Sort.mergesort
46
+ - Dual-Pivot Quicksort - Algorithms::Sort.dualpivotquicksort
46
47
  * String algorithms
47
48
  - Levenshtein distance - Algorithms::String.levenshtein_dist
48
49
  =end
@@ -235,4 +235,134 @@ module Algorithms::Sort
235
235
  sorted + left + right
236
236
  end
237
237
 
238
+ # Dual-Pivot Quicksort is a variation of Quicksort by Vladimir Yaroslavskiy.
239
+ # This is an implementation of the algorithm as it was found in the original
240
+ # research paper:
241
+ #
242
+ # http://iaroslavski.narod.ru/quicksort/DualPivotQuicksort.pdf
243
+ #
244
+ # Mirror:
245
+ # http://codeblab.com/wp-content/uploads/2009/09/DualPivotQuicksort.pdf
246
+ #
247
+ # "This algorithm offers O(n log(n)) performance on many data sets that cause
248
+ # other quicksorts to degrade to quadratic performance, and is typically
249
+ # faster than traditional (one-pivot) Quicksort implementations."
250
+ # -- http://download.oracle.com/javase/7/docs/api/java/util/Arrays.html
251
+ #
252
+ # The algorithm was improved by Vladimir Yaroslavskiy, Jon Bentley, and
253
+ # Joshua Bloch, and was implemented as the default sort algorithm for
254
+ # primatives in Java 7.
255
+ #
256
+ # Implementation in the Java JDK as of November, 2011:
257
+ # http://www.docjar.com/html/api/java/util/DualPivotQuicksort.java.html
258
+ #
259
+ # It is proved that for the Dual-Pivot Quicksort the average number
260
+ # of comparisons is 2*n*ln(n), the average number of swaps is
261
+ # 0.8*n*ln(n), whereas classical Quicksort algorithm has 2*n*ln(n)
262
+ # and 1*n*ln(n) respectively. This has been fully examined mathematically
263
+ # and experimentally.
264
+ #
265
+ # Requirements: Container should implement #pop and include the Enumerable module.
266
+ # Time Complexity: О(n log n) average, О(n log n) worst-case
267
+ # Space Complexity: О(n) auxiliary
268
+ #
269
+ # Stable: No
270
+ #
271
+ # Algorithms::Sort.dualpivotquicksort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
272
+
273
+ def self.dualpivotquicksort(container)
274
+ return container if container.size <= 1
275
+ dualpivot(container, 0, container.size-1, 3)
276
+ end
277
+
278
+ def self.dualpivot(container, left=0, right=container.size-1, div=3)
279
+ length = right - left
280
+ if length < 27 # insertion sort for tiny array
281
+ container.each_with_index do |data,i|
282
+ j = i - 1
283
+ while j >= 0
284
+ break if container[j] <= data
285
+ container[j + 1] = container[j]
286
+ j = j - 1
287
+ end
288
+ container[j + 1] = data
289
+ end
290
+ else # full dual-pivot quicksort
291
+ third = length / div
292
+ # medians
293
+ m1 = left + third
294
+ m2 = right - third
295
+ if m1 <= left
296
+ m1 = left + 1
297
+ end
298
+ if m2 >= right
299
+ m2 = right - 1
300
+ end
301
+ if container[m1] < container[m2]
302
+ dualpivot_swap(container, m1, left)
303
+ dualpivot_swap(container, m2, right)
304
+ else
305
+ dualpivot_swap(container, m1, right)
306
+ dualpivot_swap(container, m2, left)
307
+ end
308
+ # pivots
309
+ pivot1 = container[left]
310
+ pivot2 = container[right]
311
+ # pointers
312
+ less = left + 1
313
+ great = right - 1
314
+ # sorting
315
+ k = less
316
+ while k <= great
317
+ if container[k] < pivot1
318
+ dualpivot_swap(container, k, less += 1)
319
+ elsif container[k] > pivot2
320
+ while k < great && container[great] > pivot2
321
+ great -= 1
322
+ end
323
+ dualpivot_swap(container, k, great -= 1)
324
+ if container[k] < pivot1
325
+ dualpivot_swap(container, k, less += 1)
326
+ end
327
+ end
328
+ k += 1
329
+ end
330
+ # swaps
331
+ dist = great - less
332
+ if dist < 13
333
+ div += 1
334
+ end
335
+ dualpivot_swap(container, less-1, left)
336
+ dualpivot_swap(container, great+1, right)
337
+ # subarrays
338
+ dualpivot(container, left, less-2, div)
339
+ dualpivot(container, great+2, right, div)
340
+ # equal elements
341
+ if dist > length - 13 && pivot1 != pivot2
342
+ for k in less..great do
343
+ if container[k] == pivot1
344
+ dualpivot_swap(container, k, less)
345
+ less += 1
346
+ elsif container[k] == pivot2
347
+ dualpivot_swap(container, k, great)
348
+ great -= 1
349
+ if container[k] == pivot1
350
+ dualpivot_swap(container, k, less)
351
+ less += 1
352
+ end
353
+ end
354
+ end
355
+ end
356
+ # subarray
357
+ if pivot1 < pivot2
358
+ dualpivot(container, less, great, div)
359
+ end
360
+ container
361
+ end
362
+ end
363
+
364
+ def self.dualpivot_swap(container, i, j)
365
+ container[i], container[j] = container[j], container[i]
366
+ end
238
367
  end
368
+
@@ -12,7 +12,7 @@ if defined? Containers::CDeque
12
12
  # Check if any instances were swept
13
13
  count = 0
14
14
  ObjectSpace.each_object(anon_class) { |x| count += 1 }
15
- count.should eql(100)
15
+ expect(count).to eql(100)
16
16
  end
17
17
  end
18
18
  end
data/spec/deque_spec.rb CHANGED
@@ -3,31 +3,31 @@ require 'algorithms'
3
3
 
4
4
  shared_examples "(empty deque)" do
5
5
  it "should return nil when popping objects" do
6
- @deque.pop_front.should be_nil
7
- @deque.pop_back.should be_nil
6
+ expect(@deque.pop_front).to be_nil
7
+ expect(@deque.pop_back).to be_nil
8
8
  end
9
9
 
10
10
  it "should return a size of 1 when sent #push_front" do
11
11
  @deque.push_front(1)
12
- @deque.size.should eql(1)
12
+ expect(@deque.size).to eql(1)
13
13
  end
14
14
 
15
15
  it "should return a size of 1 when sent #push_back" do
16
16
  @deque.push_back(1)
17
- @deque.size.should eql(1)
17
+ expect(@deque.size).to eql(1)
18
18
  end
19
19
 
20
20
  it "should return nil when sent #front and #back" do
21
- @deque.front.should be_nil
22
- @deque.back.should be_nil
21
+ expect(@deque.front).to be_nil
22
+ expect(@deque.back).to be_nil
23
23
  end
24
24
 
25
25
  it "should be empty" do
26
- @deque.should be_empty
26
+ expect(@deque).to be_empty
27
27
  end
28
28
 
29
29
  it "should raise ArgumentError if passed more than one argument" do
30
- lambda { @deque.class.send("new", Time.now, []) }.should raise_error
30
+ expect { @deque.class.send("new", Time.now, []) }.to raise_error(ArgumentError)
31
31
  end
32
32
  end
33
33
 
@@ -38,40 +38,40 @@ shared_examples "(non-empty deque)" do
38
38
  end
39
39
 
40
40
  it "should return last pushed object with pop_back" do
41
- @deque.pop_back.should eql("10")
42
- @deque.pop_back.should eql(10)
41
+ expect(@deque.pop_back).to eql("10")
42
+ expect(@deque.pop_back).to eql(10)
43
43
  end
44
44
 
45
45
  it "should return first pushed object with pop_front" do
46
- @deque.pop_front.should eql(10)
47
- @deque.pop_front.should eql("10")
46
+ expect(@deque.pop_front).to eql(10)
47
+ expect(@deque.pop_front).to eql("10")
48
48
  end
49
49
 
50
50
  it "should return a size greater than 0" do
51
- @deque.size.should eql(2)
51
+ expect(@deque.size).to eql(2)
52
52
  end
53
53
 
54
54
  it "should not be empty" do
55
- @deque.should_not be_empty
55
+ expect(@deque).not_to be_empty
56
56
  end
57
57
 
58
58
  it "should iterate in LIFO order with #each_backward" do
59
59
  arr = []
60
60
  @deque.each_backward { |obj| arr << obj }
61
- arr.should eql(["10", 10])
61
+ expect(arr).to eql(["10", 10])
62
62
  end
63
63
 
64
64
  it "should iterate in FIFO order with #each_forward" do
65
65
  arr = []
66
66
  @deque.each_forward { |obj| arr << obj }
67
- arr.should eql([10, "10"])
67
+ expect(arr).to eql([10, "10"])
68
68
  end
69
69
 
70
70
  it "should return nil after everything's popped" do
71
71
  @deque.pop_back
72
72
  @deque.pop_back
73
- @deque.pop_back.should be_nil
74
- @deque.front.should be_nil
73
+ expect(@deque.pop_back).to be_nil
74
+ expect(@deque.front).to be_nil
75
75
  end
76
76
  end
77
77