Commit e71d6401 authored by Russ Cox's avatar Russ Cox

sort: improve average quicksort performance

Revert "Revert "sort: improve average quicksort performance""
This reverts commit 30b87bb9.

See https://golang.org/cl/15688 for the CL being replayed.

Change-Id: I2ba36334003f4971f95a10536adfdd86be9a50de
Reviewed-on: https://go-review.googlesource.com/17389
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent 43a9e998
...@@ -122,10 +122,10 @@ func Example_sortMultiKeys() { ...@@ -122,10 +122,10 @@ func Example_sortMultiKeys() {
fmt.Println("By language,<lines,user:", changes) fmt.Println("By language,<lines,user:", changes)
// Output: // Output:
// By user: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken Go 200} {ken C 150} {r Go 100} {r C 150} {rsc Go 200}] // By user: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}]
// By user,<lines: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}] // By user,<lines: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}]
// By user,>lines: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken Go 200} {ken C 150} {r C 150} {r Go 100} {rsc Go 200}] // By user,>lines: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken Go 200} {ken C 150} {r C 150} {r Go 100} {rsc Go 200}]
// By language,<lines: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {ken Go 200} {glenda Go 200} {rsc Go 200} {gri Smalltalk 80}] // By language,<lines: [{dmr C 100} {ken C 150} {r C 150} {r Go 100} {gri Go 100} {ken Go 200} {glenda Go 200} {rsc Go 200} {gri Smalltalk 80}]
// By language,<lines,user: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {glenda Go 200} {ken Go 200} {rsc Go 200} {gri Smalltalk 80}] // By language,<lines,user: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {glenda Go 200} {ken Go 200} {rsc Go 200} {gri Smalltalk 80}]
} }
...@@ -72,7 +72,7 @@ func heapSort(data Interface, a, b int) { ...@@ -72,7 +72,7 @@ func heapSort(data Interface, a, b int) {
} }
} }
// Quicksort, following Bentley and McIlroy, // Quicksort, loosely following Bentley and McIlroy,
// ``Engineering a Sort Function,'' SP&E November 1993. // ``Engineering a Sort Function,'' SP&E November 1993.
// medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1]. // medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1].
...@@ -111,59 +111,82 @@ func doPivot(data Interface, lo, hi int) (midlo, midhi int) { ...@@ -111,59 +111,82 @@ func doPivot(data Interface, lo, hi int) (midlo, midhi int) {
// Invariants are: // Invariants are:
// data[lo] = pivot (set up by ChoosePivot) // data[lo] = pivot (set up by ChoosePivot)
// data[lo <= i < a] = pivot // data[lo < i < a] < pivot
// data[a <= i < b] < pivot // data[a <= i < b] <= pivot
// data[b <= i < c] is unexamined // data[b <= i < c] unexamined
// data[c <= i < d] > pivot // data[c <= i < hi-1] > pivot
// data[d <= i < hi] = pivot // data[hi-1] >= pivot
//
// Once b meets c, can swap the "= pivot" sections
// into the middle of the slice.
pivot := lo pivot := lo
a, b, c, d := lo+1, lo+1, hi, hi a, c := lo+1, hi-1
for ; a != c && data.Less(a, pivot); a++ {
}
b := a
for { for {
for b < c { for ; b != c && !data.Less(pivot, b); b++ { // data[b] <= pivot
if data.Less(b, pivot) { // data[b] < pivot
b++
} else if !data.Less(pivot, b) { // data[b] = pivot
data.Swap(a, b)
a++
b++
} else {
break
}
} }
for b < c { for ; b != c && data.Less(pivot, c-1); c-- { // data[c-1] > pivot
if data.Less(pivot, c-1) { // data[c-1] > pivot
c--
} else if !data.Less(c-1, pivot) { // data[c-1] = pivot
data.Swap(c-1, d-1)
c--
d--
} else {
break
}
} }
if b >= c { if b == c {
break break
} }
// data[b] > pivot; data[c-1] < pivot // data[b] > pivot; data[c-1] <= pivot
data.Swap(b, c-1) data.Swap(b, c-1)
b++ b++
c-- c--
} }
// If hi-c<3 then there are duplicates (by property of median of nine).
n := min(b-a, a-lo) // Let be a bit more conservative, and set border to 5.
swapRange(data, lo, b-n, n) protect := hi-c < 5
if !protect && hi-c < (hi-lo)/4 {
n = min(hi-d, d-c) // Lets test some points for equality to pivot
swapRange(data, c, hi-n, n) dups := 0
if !data.Less(pivot, hi-1) { // data[hi-1] = pivot
return lo + b - a, hi - (d - c) data.Swap(c, hi-1)
c++
dups++
}
if !data.Less(b-1, pivot) { // data[b-1] = pivot
b--
dups++
}
// m-lo = (hi-lo)/2 > 6
// b-lo > (hi-lo)*3/4-1 > 8
// ==> m < b ==> data[m] <= pivot
if !data.Less(m, pivot) { // data[m] = pivot
data.Swap(m, b-1)
b--
dups++
}
// if at least 2 points are equal to pivot, assume skewed distribution
protect = dups > 1
}
if protect {
// Protect against a lot of duplicates
// Add invariant:
// data[a <= i < b] unexamined
// data[b <= i < c] = pivot
for {
for ; a != b && !data.Less(b-1, pivot); b-- { // data[b] == pivot
}
for ; a != b && data.Less(a, pivot); a++ { // data[a] < pivot
}
if a == b {
break
}
// data[a] == pivot; data[b-1] < pivot
data.Swap(a, b-1)
a++
b--
}
}
// Swap pivot into middle
data.Swap(pivot, b-1)
return b - 1, c
} }
func quickSort(data Interface, a, b, maxDepth int) { func quickSort(data Interface, a, b, maxDepth int) {
for b-a > 7 { for b-a > 12 { // Use ShellSort for slices <= 12 elements
if maxDepth == 0 { if maxDepth == 0 {
heapSort(data, a, b) heapSort(data, a, b)
return return
...@@ -181,6 +204,13 @@ func quickSort(data Interface, a, b, maxDepth int) { ...@@ -181,6 +204,13 @@ func quickSort(data Interface, a, b, maxDepth int) {
} }
} }
if b-a > 1 { if b-a > 1 {
// Do ShellSort pass with gap 6
// It could be written in this simplified form cause b-a <= 12
for i := a + 6; i < b; i++ {
if data.Less(i, i-6) {
data.Swap(i, i-6)
}
}
insertionSort(data, a, b) insertionSort(data, a, b)
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment