Skip to content

Commit

Permalink
Rename sortedPrefix methods (#77)
Browse files Browse the repository at this point in the history
This renames sortedPrefix to min(count:), adds max(count:),
and moves both methods down to Sequence.

In order to still provide the optimization when `count` is close to
a collection's length, we include a Collection-specific overload of
all methods. These won't be accessed when in a Sequence-generic context,
but it's the best we can do without adding these methods as protocol
requirements.
  • Loading branch information
natecook1000 authored Feb 26, 2021
1 parent 32837b9 commit bd64bef
Show file tree
Hide file tree
Showing 7 changed files with 631 additions and 449 deletions.
58 changes: 58 additions & 0 deletions Guides/MinMax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Min/Max with Count

[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/MinMax.swift) |
[Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/MinMaxTests.swift)]

Returns the smallest or largest elements of this collection, sorted by a predicate or in the order defined by `Comparable` conformance.

If you need to sort a collection but only need access to a prefix or suffix of the sorted elements, using these methods can give you a performance boost over sorting the entire collection. The order of equal elements is guaranteed to be preserved.

```swift
let numbers = [7, 1, 6, 2, 8, 3, 9]
let smallestThree = numbers.min(count: 3, sortedBy: <)
// [1, 2, 3]
```

## Detailed Design

This adds the `Collection` methods shown below:

```swift
extension Collection {
public func min(
count: Int,
sortedBy areInIncreasingOrder: (Element, Element) throws -> Bool
) rethrows -> [Element]

public func max(
count: Int,
sortedBy areInIncreasingOrder: (Element, Element) throws -> Bool
) rethrows -> [Element]
}
```

Additionally, versions of these methods for `Comparable` types are also provided:

```swift
extension Collection where Element: Comparable {
public func min(count: Int) -> [Element]

public func max(count: Int) -> [Element]
}
```

### Complexity

The algorithm used is based on [Soroush Khanlou's research on this matter](https://khanlou.com/2018/12/analyzing-complexity/). The total complexity is `O(k log k + nk)`, which will result in a runtime close to `O(n)` if *k* is a small amount. If *k* is a large amount (more than 10% of the collection), we fall back to sorting the entire array. Realistically, this means the worst case is actually `O(n log n)`.

Here are some benchmarks we made that demonstrates how this implementation (SmallestM) behaves when *k* increases (before implementing the fallback):

![Benchmark](Resources/SortedPrefix/FewElements.png)
![Benchmark 2](Resources/SortedPrefix/ManyElements.png)

### Comparison with other languages

**C++:** The `<algorithm>` library defines a `partial_sort` function where the entire array is returned using a partial heap sort.

**Python:** Defines a `heapq` priority queue that can be used to manually achieve the same result.

48 changes: 0 additions & 48 deletions Guides/SortedPrefix.md

This file was deleted.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Read more about the package, and the intent behind it, in the [announcement on s

#### Partial sorting

- [`sortedPrefix(_:by:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/SortedPrefix.md): Returns the first k elements of a sorted collection.
- [`min(count:)`, `max(count:)`, `min(count:sortedBy:)`, `max(count:sortedBy:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/MinMax.md): Returns the smallest or largest elements of a collection, sorted by a predicate.

#### Other useful operations

Expand Down
Loading

0 comments on commit bd64bef

Please sign in to comment.