From f299df1a8924ef9d4c5748325b09b6d86125fc28 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Fri, 9 Oct 2020 14:44:42 +0200 Subject: [PATCH 1/2] Guide docs --- Guides/PartialSort.md | 59 ++++++++++++++++++++++++++++ README.md | 4 ++ Sources/Algorithms/PartialSort.swift | 6 +-- 3 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 Guides/PartialSort.md diff --git a/Guides/PartialSort.md b/Guides/PartialSort.md new file mode 100644 index 00000000..39df4ff4 --- /dev/null +++ b/Guides/PartialSort.md @@ -0,0 +1,59 @@ +# Partial Sort + +[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/PartialSort.swift) | + [Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/PartialSortTests.swift)] + +Returns a collection such that the `0...k` range contains the first k sorted elements of a sequence. +The order of equal elements is not guaranteed to be preserved, and the order of the remaining elements is unspecified. + +If you need to sort a sequence but only need access to a prefix of its elements, +using this method can give you a performance boost over sorting the entire sequence. + +```swift +let numbers = [7,1,6,2,8,3,9] +let almostSorted = numbers.partiallySorted(3, <) +// [1, 2, 3, 9, 7, 6, 8] +``` + +## Detailed Design + +This adds the in place `MutableCollection` method shown below: + +```swift +extension Sequence { + func partiallySort(_ count: Int, by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows +} +``` + +Additionally, versions of this method that return a new array and abstractions for `Comparable` types are also provided: + +```swift +extension MutableCollection where Self: RandomAccessCollection, Element: Comparable { + public mutating func partiallySort(_ count: Int) +} + +extension Sequence { + public func partiallySorted(_ count: Int, by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element] +} + +extension Sequence where Element: Comparable { + public func partiallySorted(_ count: Int) -> [Element] +} +``` + +### Complexity + +Partially sorting is a O(_k log n_) operation, where _k_ is the number of elements to sort +and _n_ is the length of the sequence. + +`partiallySort(_:by:)` is a slight generalization of a priority queue. It's implemented +as an in line heapsort that stops after _k_ runs. + +### Comparison with other languages + +**C++:** The `` library defines a `partial_sort` function with similar +semantics to this one. + +**Python:** Defines a `heapq` priority queue that can be used to manually +achieve the same result. + diff --git a/README.md b/README.md index 6bc15bc8..99ace059 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,10 @@ Read more about the package, and the intent behind it, in the [announcement on s - [`randomStableSample(count:)`, `randomStableSample(count:using:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/RandomSampling.md): Randomly selects a specific number of elements from a collection, preserving their original relative order. - [`uniqued()`, `uniqued(on:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/Unique.md): The unique elements of a collection, preserving their order. +#### Partial sorting + +- [`partiallySorted(_:by:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/PartialSort.md): Sorts a sequence only up to a specific index, leaving the remaining elements unsorted. + #### Other useful operations - [`chunked(by:)`, `chunked(on:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/Chunked.md): Eager and lazy operations that break a collection into chunks based on either a binary predicate or when the result of a projection changes. diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index 22ad55e6..b0f27b96 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -136,8 +136,8 @@ extension MutableCollection where Self: RandomAccessCollection, Element: Compara extension MutableCollection where Self: RandomAccessCollection, Index == Int { typealias Priority = (Element, Element) throws -> Bool - /// Partially sorts this array by using an in place heapsort that stops after we find the desired k amount - /// of elements. The heap is stored and processed in reverse order so that the array doesn't have to be flipped + /// Partially sorts this collection by using an in place heapsort that stops after we find the desired k amount + /// of elements. The heap is stored and processed in reverse order so that the collection doesn't have to be flipped /// once the final result is found. /// /// Complexity: O(k log n) @@ -164,7 +164,7 @@ extension MutableCollection where Self: RandomAccessCollection, Index == Int { } /// Sifts down an element from this heap. - /// The heap is stored in reverse order, so sifting down will actually move the element up in the heap array. + /// The heap is stored in reverse order, so sifting down will actually move the element up in the heap. /// /// - Parameter i: The element index to sift down /// - Parameter by: The predicate to use when determining the priority of elements in the heap From 6cd2870ef69bd413bf915acbc020ec0c8eea0fb1 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Fri, 9 Oct 2020 14:49:43 +0200 Subject: [PATCH 2/2] Use Indexes --- Sources/Algorithms/PartialSort.swift | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Sources/Algorithms/PartialSort.swift b/Sources/Algorithms/PartialSort.swift index b0f27b96..232e122f 100644 --- a/Sources/Algorithms/PartialSort.swift +++ b/Sources/Algorithms/PartialSort.swift @@ -70,7 +70,7 @@ extension Sequence where Element: Comparable { } } -extension MutableCollection where Self: RandomAccessCollection, Index == Int { +extension MutableCollection where Self: RandomAccessCollection { /// Rearranges this collection such that the 0...k range contains the first /// k sorted elements in this collection, using the given predicate as the /// comparison between elements. @@ -101,7 +101,7 @@ extension MutableCollection where Self: RandomAccessCollection, Index == Int { } } -extension MutableCollection where Self: RandomAccessCollection, Element: Comparable, Index == Int { +extension MutableCollection where Self: RandomAccessCollection, Element: Comparable { /// Rearranges this collection such that the 0...k range contains the first /// k smallest elements in this collection. /// @@ -133,7 +133,7 @@ extension MutableCollection where Self: RandomAccessCollection, Element: Compara // __partiallySort(_:by:) //===----------------------------------------------------------------------===// -extension MutableCollection where Self: RandomAccessCollection, Index == Int { +extension MutableCollection where Self: RandomAccessCollection { typealias Priority = (Element, Element) throws -> Bool /// Partially sorts this collection by using an in place heapsort that stops after we find the desired k amount @@ -154,11 +154,11 @@ extension MutableCollection where Self: RandomAccessCollection, Index == Int { } var iterator = (0..= heapEndIndex else { return parent } - guard try priority(self[child], self[parent]) else { + let childElement = self[index(startIndex, offsetBy: child)] + let parentElement = self[index(startIndex, offsetBy: parent)] + guard try priority(childElement, parentElement) else { return parent } return child