Skip to content

Commit da748c1

Browse files
author
mhutchin
committed
Add dualpivotquicksort (Dual-Pivot Quicksort)
Dual-Pivot Quicksort is a variation of Quicksort by Vladimir Yaroslavskiy. "This algorithm offers O(n log(n)) performance on many data sets that cause other quicksorts to degrade to quadratic performance, and is typically faster than traditional (one-pivot) Quicksort implementations." -- http://download.oracle.com/javase/7/docs/api/java/util/Arrays.html
1 parent 51a6d60 commit da748c1

File tree

4 files changed

+134
-1
lines changed

4 files changed

+134
-1
lines changed

README.markdown

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ compare performance in different situations.
4141
- Shell sort Algorithms::Sort.shell_sort
4242
- Quicksort Algorithms::Sort.quicksort
4343
- Mergesort Algorithms::Sort.mergesort
44+
- Dual-Pivot Quicksort Algorithms::Sort.dualpivotquicksort
4445

4546
## SYNOPSIS:
4647

lib/algorithms.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
- Shell sort - Algorithms::Sort.shell_sort
4444
- Quicksort - Algorithms::Sort.quicksort
4545
- Mergesort - Algorithms::Sort.mergesort
46+
- Dual-Pivot Quicksort - Algorithms::Sort.dualpivotquicksort
4647
* String algorithms
4748
- Levenshtein distance - Algorithms::String.levenshtein_dist
4849
=end

lib/algorithms/sort.rb

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,4 +235,134 @@ def self.merge(left, right)
235235
sorted + left + right
236236
end
237237

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
238367
end
368+

spec/sort_spec.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
describe "sort algorithms" do
66
before(:each) do
7-
@sorts = %w(bubble_sort comb_sort selection_sort heapsort insertion_sort shell_sort quicksort mergesort)
7+
@sorts = %w(bubble_sort comb_sort selection_sort heapsort insertion_sort
8+
shell_sort quicksort mergesort dualpivotquicksort)
89
end
910

1011
it "should work for empty containers" do

0 commit comments

Comments
 (0)