Skip to content

Commit cb95ad7

Browse files
committed
Merge pull request kanwei#17 from mhutchin/adddualpivotquicksort
Add dualpivotquicksort (Dual-Pivot Quicksort)
2 parents 51a6d60 + da748c1 commit cb95ad7

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)