@@ -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
238367end
368+
0 commit comments