From 91fc5a34f9af9852d4afb1f514f9d6ecc7e83ec2 Mon Sep 17 00:00:00 2001 From: Nicolau Leal Werneck Date: Fri, 26 Jul 2019 13:02:52 -0400 Subject: [PATCH] Implemented CombSort as an additional sorting algorithm in base --- base/exports.jl | 1 + base/ordering.jl | 17 ++++++++++++++++- base/sort.jl | 34 +++++++++++++++++++++++++++++++++- test/sorting.jl | 6 +++--- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/base/exports.jl b/base/exports.jl index 23b4141a06c5e..06b43886261fc 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -34,6 +34,7 @@ export Channel, Cmd, Colon, + CombSort, Complex, ComplexF64, ComplexF32, diff --git a/base/ordering.jl b/base/ordering.jl index 52320ac83ae89..d0cb6e91e12e6 100644 --- a/base/ordering.jl +++ b/base/ordering.jl @@ -16,7 +16,7 @@ export # not exported by Base By, Lt, Perm, ReverseOrdering, ForwardOrdering, DirectOrdering, - lt, ord, ordtype + lt, ltminmax, ord, ordtype abstract type Ordering end @@ -57,6 +57,21 @@ lt(o::Lt, a, b) = o.lt(a,b) lt(p.order, da, db) | (!lt(p.order, db, da) & (a < b)) end +ltminmax(o::ForwardOrdering, a, b) = min(a,b), max(a,b) +ltminmax(o::ReverseOrdering, a, b) = max(a,b), min(a,b) +ltminmax(o::By, a, b) = ifelse(isless(o.by(a),o.by(b)), a, b), ifelse(isless(o.by(a),o.by(b)), b, a) +ltminmax(o::Lt, a, b) = ifelse(o.lt(a,b), a, b), ifelse(o.lt(a,b), b, a) + +@propagate_inbounds function ltminmax(p::Perm, a::Integer, b::Integer) + da = p.data[a] + db = p.data[b] + if (lt(p.order, da, db) | (!lt(p.order, db, da) & (a < b))) + (a, b) + else + (b, a) + end +end + ordtype(o::ReverseOrdering, vs::AbstractArray) = ordtype(o.fwd, vs) ordtype(o::Perm, vs::AbstractArray) = ordtype(o.order, o.data) # TODO: here, we really want the return type of o.by, without calling it diff --git a/base/sort.jl b/base/sort.jl index 772faa37c85ce..33c884e5f7595 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -40,6 +40,7 @@ export # also exported by Base InsertionSort, QuickSort, MergeSort, + CombSort, PartialQuickSort export # not exported by Base @@ -385,6 +386,7 @@ abstract type Algorithm end struct InsertionSortAlg <: Algorithm end struct QuickSortAlg <: Algorithm end struct MergeSortAlg <: Algorithm end +struct CombSortAlg <: Algorithm end """ PartialQuickSort{T <: Union{Int,OrdinalRange}} @@ -454,6 +456,24 @@ Characteristics: * *divide-and-conquer* sort strategy. """ const MergeSort = MergeSortAlg() +""" + CombSort + +Indicate that a sorting function should use the comb sort +algorithm. Comb sort traverses the collection multiple times +ordering pairs of elements with a given interval between them. +The interval decreases exponentially until it becomes 1, then +it switches to insertion sort on the whole input. + +Characteristics: + * *not stable*: does not preserve the ordering of elements which + compare equal (e.g. "a" and "A" in a sort of letters which + ignores case). + * *in-place* in memory. + * *parallelizable* this algorithm is suitable for vectorization + because it performs many independent comparisons. +""" +const CombSort = CombSortAlg() const DEFAULT_UNSTABLE = QuickSort const DEFAULT_STABLE = MergeSort @@ -608,7 +628,6 @@ function sort!(v::AbstractVector, lo::Int, hi::Int, a::PartialQuickSort{Int}, return v end - function sort!(v::AbstractVector, lo::Int, hi::Int, a::PartialQuickSort{T}, o::Ordering) where T<:OrdinalRange @inbounds while lo < hi @@ -632,6 +651,19 @@ function sort!(v::AbstractVector, lo::Int, hi::Int, a::PartialQuickSort{T}, return v end +function sort!(v::AbstractVector, lo::Int, hi::Int, ::CombSortAlg, o::Ordering) + interval = (3 * (hi-lo+1)) >> 2 + + @inbounds while interval > 1 + for j in lo:hi-interval + v[j], v[j+interval] = ltminmax(o, v[j], v[j+interval]) + end + interval = (3 * interval) >> 2 + end + + return sort!(v, lo, hi, InsertionSort, o) +end + ## generic sorting methods ## diff --git a/test/sorting.jl b/test/sorting.jl index 8b58a2d2620ef..781359e8f405b 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -187,7 +187,7 @@ Base.step(r::ConstantRange) = 0 end @testset "unstable algorithms" begin - for alg in [QuickSort, PartialQuickSort(length(a))] + for alg in [QuickSort, PartialQuickSort(length(a)), CombSort] b = sort(a, alg=alg) @test issorted(b) b = sort(a, alg=alg, rev=true) @@ -265,7 +265,7 @@ end end # unstable algorithms - for alg in [QuickSort, PartialQuickSort(n)] + for alg in [QuickSort, PartialQuickSort(n), CombSort] p = sortperm(v, alg=alg, rev=rev) @test p == sortperm(float(v), alg=alg, rev=rev) @test isperm(p) @@ -279,7 +279,7 @@ end end v = randn_with_nans(n,0.1) - # TODO: alg = PartialQuickSort(n) fails here + # TODO: alg = PartialQuickSort(n) and CombSort fail here for alg in [InsertionSort, QuickSort, MergeSort], rev in [false,true] # test float sorting with NaNs