algorithms 0.6.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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