Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Topic/sorting 721 #738

Merged
merged 12 commits into from
Aug 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 116 additions & 35 deletions core/src/main/scala/spire/math/Sorting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,32 @@ trait Sort extends Any {
}

/**
* Simple implementation of insertion sort.
* An implementation of insertion sort.
*
* Works for small arrays but due to O(n^2) complexity is not generally good.
* Works well for small arrays but due to quadratic complexity is not generally optimal.
*/
object InsertionSort extends Sort {

/**
* Sorts `data` in place using insertion sort.
*
* @param data the array to be sorted
* @tparam A a member of the type class `Order`
*/
final def sort[@sp A:Order:ClassTag](data:Array[A]): Unit =
sort(data, 0, data.length)

/**
* Uses insertion sort on `data` to sort the entries from the index `start`
* up to, but not including, the index `end`. Operates in place.
*
* @param data the array to be sorted
* @param start the index of the first element, inclusive, to be sorted
* @param end the index of the last element, exclusive, to be sorted
* @tparam A a member of the type class `Order`
*/
final def sort[@sp A](data:Array[A], start:Int, end:Int)(implicit o:Order[A], ct:ClassTag[A]): Unit = {

require(start <= end && start >= 0 && end <= data.length)
var i = start + 1
while (i < end) {
val item = data(i)
Expand All @@ -46,10 +62,22 @@ object MergeSort extends Sort {
@inline final def startWidth: Int = 8
@inline final def startStep: Int = 16

/**
* Uses merge sort to sort the array `data` in place.
*
* If the size of the input array does not exceed the threshold `startStep`,
* uses insertion sort instead.
*
* @param data the array to be sorted
* @tparam A a member of the type class `Order`
*/
final def sort[@sp A:Order:ClassTag](data:Array[A]): Unit = {
val len = data.length

if (len <= startStep) return InsertionSort.sort(data)
if (len <= startStep) {
InsertionSort.sort(data)
return
}

var buf1:Array[A] = data
var buf2:Array[A] = new Array[A](len)
Expand Down Expand Up @@ -78,17 +106,27 @@ object MergeSort extends Sort {
step *= 2
}

if (buf1 != data) System.arraycopy(buf1, 0, data, 0, len)
if (!buf1.eq(data)) System.arraycopy(buf1, 0, data, 0, len)
}

/**
* Helper method for mergeSort, used to do a single "merge" between two
* sections of the input array. The start, mid and end parameters denote the
* left and right ranges of the input to merge, as well as the area of the
* ouput to write to.
*/
* Helper method for mergeSort, used to do a single "merge" between two
* sections of the input array, and write the result to the output array.
*
* The first input section starts at `start` (inclusive) and ends at `mid` (exclusive).
* The second input section starts at `mid` (inclusive) and ends at `end` (exclusive).
*
* Writing to the output begins at `start` (inclusive).
*
* @param in the input array
* @param out the output array
* @param start the start of the first input section (inclusive) as well as the start of the merged output
* @param mid the end of the first input section (exclusive) and the beginning of the second input section (inclusive)
* @param end the end of the second input section (exclusive)
* @tparam A a member of the type class `Order`
*/
@inline final def merge[@sp A](in:Array[A], out:Array[A], start:Int, mid:Int, end:Int)(implicit o:Order[A]): Unit = {

require(start >= 0 && start <= mid && mid <= end && end <= in.length && end <= out.length)
var ii = start
var jj = mid
var kk = start
Expand All @@ -111,37 +149,74 @@ object MergeSort extends Sort {
object QuickSort {
@inline final def limit: Int = 16

final def sort[@sp A:Order:ClassTag](data:Array[A]): Unit = qsort(data, 0, data.length - 1)

final def qsort[@sp A](data:Array[A], left: Int, right: Int)(implicit o:Order[A], ct:ClassTag[A]): Unit = {
/**
* Uses quicksort on `data` to sort the entries. Operates in place.
*
* If the size of the input array is less than the threshold `limit`,
* uses insertion sort instead.
*
* @param data the array to be sorted
* @tparam A a member of the type class `Order`
*/
final def sort[@sp A:Order:ClassTag](data:Array[A]): Unit = qsort(data, 0, data.length)

if (right - left < limit) return InsertionSort.sort(data, left, right + 1)
/**
* Uses quicksort on `data` to sort the entries from the index `start`
* up to, but not including, the index `end`. Operates in place.
*
* If the size of the segment to be sorted is less than the threshold `limit`
* uses insertion sort instead.
*
* @param data the array to be sorted
* @param start the index from which to start sorting (inclusive)
* @param end the index at which to stop sorting (exclusive)
* @tparam A a member of the type class `Order`
*/
final def qsort[@sp A](data:Array[A], start: Int, end: Int)(implicit o:Order[A], ct:ClassTag[A]): Unit = {
require(start >= 0 && end <= data.length)
if (end - start < limit) {
InsertionSort.sort(data, start, end)
return
}

val pivot = left + (right - left) / 2
val next = partition(data, left, right, pivot)
qsort(data, left, next - 1)
qsort(data, next + 1, right)
val pivotIndex = start + (end - start) / 2
val nextPivotIndex = partition(data, start, end, pivotIndex)
qsort(data, start, nextPivotIndex)
qsort(data, nextPivotIndex + 1, end)
}

final def partition[@sp A](data:Array[A], left:Int, right:Int, pivot:Int)(implicit o:Order[A], ct:ClassTag[A]): Int = {

val value = data(pivot)

//swap(pivot, right)
var tmp = data(pivot); data(pivot) = data(right); data(right) = tmp

var store = left
var i = left
while (i < right) {
if (o.lt(data(i), value)) {
/**
* Helper method for the quick sort implementation. Partitions the segment of the array `data` from `start` to `end`
* according to the value at the given `pivotIndex`. Values in the segment less than the pivot value will end up
* to the left of the pivot value, and values greater on the right. Operates in place.
*
* @param data the array to be partitioned
* @param start the left endpoint (inclusive) of the interval to be partitioned
* @param end the right endpoint (exclusive) of the interval to be partitioned
* @param pivotIndex the index of the current pivot
* @tparam A a member of the type class Order
* @return the next pivot value
*/
final def partition[@sp A](data:Array[A], start:Int, end:Int, pivotIndex:Int)(implicit o:Order[A], ct:ClassTag[A]): Int = {
require(start >= 0 && pivotIndex >= start && end > pivotIndex && end <= data.length)
val pivotValue = data(pivotIndex)

data(pivotIndex) = data(end - 1)

var temp = pivotValue
var store = start
var i = start
while (i < end - 1) {
if (o.lt(data(i), pivotValue)) {
//swap(i, store)
tmp = data(i); data(i) = data(store); data(store) = tmp
temp = data(i); data(i) = data(store); data(store) = temp
store += 1
}
i += 1
}
//swap(store, right)
tmp = data(store); data(store) = data(right); data(right) = tmp

data(end - 1) = data(store)
data(store) = pivotValue
store
}
}
Expand All @@ -153,14 +228,20 @@ object QuickSort {
* Object providing in-place sorting capability for arrays.
*
* Sorting.sort() uses quickSort() by default (in-place, not stable, generally
* fastest but might hit bad cases where it's O(n^2)). Also provides
* fastest but might hit bad cases where it is quadratic. Also provides
* mergeSort() (in-place, stable, uses extra memory, still pretty fast) and
* insertionSort(), which is slow except for small arrays.
*/
object Sorting {
/** Delegates to [[spire.math.QuickSort.sort]] */
final def sort[@sp A:Order:ClassTag](data:Array[A]): Unit = QuickSort.sort(data)

/** Delegates to [[spire.math.InsertionSort.sort]] */
final def insertionSort[@sp A:Order:ClassTag](data:Array[A]): Unit = InsertionSort.sort(data)

/** Delegates to [[spire.math.MergeSort.sort]] */
final def mergeSort[@sp A:Order:ClassTag](data:Array[A]): Unit = MergeSort.sort(data)
final def quickSort[@sp K:Order:ClassTag](data:Array[K]): Unit = QuickSort.sort(data)

/** Delegates to [[spire.math.QuickSort.sort]] */
final def quickSort[@sp A:Order:ClassTag](data:Array[A]): Unit = QuickSort.sort(data)
}
Loading