Skip to content

Commit 62ee6f2

Browse files
committed
Change implementation and name
1 parent d4a2e6b commit 62ee6f2

File tree

2 files changed

+58
-324
lines changed

2 files changed

+58
-324
lines changed

Sources/Algorithms/PartialSort.swift

Lines changed: 25 additions & 259 deletions
Original file line numberDiff line numberDiff line change
@@ -7,280 +7,46 @@
77
//
88
// See https://swift.org/LICENSE.txt for license information
99
//
10-
//===----------------------------------------------------------------------===//
11-
12-
extension Sequence {
13-
/// Returns the elements of the sequence such that the 0...k range contains
14-
/// the first k sorted elements in this sequence, using the given predicate
15-
/// as the comparison between elements.
16-
///
17-
/// This example partially sorts an array of integers to retrieve its three
18-
/// smallest values:
19-
///
20-
/// let numbers = [7,1,6,2,8,3,9]
21-
/// let almostSorted = numbers.partiallySorted(3, <)
22-
/// // [1, 2, 3, 9, 7, 6, 8]
23-
/// let smallestThree = almostSorted.prefix(3)
24-
/// // [1, 2, 3]
25-
///
26-
/// The order of equal elements is not guaranteed to be preserved, and the
27-
/// order of the remaining elements is unspecified.
28-
///
29-
/// If you need to sort a sequence but only need access to a prefix of its
30-
/// elements, using this method can give you a performance boost over sorting
31-
/// the entire sequence.
32-
///
33-
/// - Parameter count: The k number of elements to partially sort.
34-
/// - Parameter areInIncreasingOrder: A predicate that returns true if its
35-
/// first argument should be ordered before its second argument;
36-
/// otherwise, false.
37-
///
38-
/// - Complexity: O(k log n)
39-
public func partiallySorted(
40-
_ count: Int,
41-
by areInIncreasingOrder: (Element, Element) throws -> Bool
42-
) rethrows -> [Element] {
43-
var result = ContiguousArray(self)
44-
try result.partiallySort(count, by: areInIncreasingOrder)
45-
return Array(result)
46-
}
47-
}
48-
49-
extension Sequence where Element: Comparable {
50-
/// Returns the elements of the sequence such that the 0...k range contains
51-
/// the first k smallest elements in this sequence.
52-
///
53-
/// This example partially sorts an array of integers to retrieve its three
54-
/// smallest values:
55-
///
56-
/// let numbers = [7,1,6,2,8,3,9]
57-
/// let almostSorted = numbers.partiallySorted(3)
58-
/// // [1, 2, 3, 9, 7, 6, 8]
59-
/// let smallestThree = almostSorted.prefix(3)
60-
/// // [1, 2, 3]
61-
///
62-
/// The order of equal elements is not guaranteed to be preserved, and the
63-
/// order of the remaining elements is unspecified.
64-
///
65-
/// If you need to sort a sequence but only need access to a prefix of
66-
/// its elements, using this method can give you a performance boost over
67-
/// sorting the entire sequence.
68-
///
69-
/// - Parameter count: The k number of elements to partially sort
70-
/// in ascending order.
71-
///
72-
/// - Complexity: O(k log n)
73-
public func partiallySorted(_ count: Int) -> [Element] {
74-
return partiallySorted(count, by: <)
75-
}
76-
}
77-
78-
extension MutableCollection where Self: RandomAccessCollection {
79-
/// Rearranges this collection such that the 0...k range contains the first
80-
/// k sorted elements in this collection, using the given predicate as the
81-
/// comparison between elements.
82-
///
83-
/// This example partially sorts an array of integers to retrieve its three
84-
/// smallest values:
85-
///
86-
/// var numbers = [7,1,6,2,8,3,9]
87-
/// numbers.partiallySort(3, <)
88-
/// // [1, 2, 3, 9, 7, 6, 8]
89-
/// let smallestThree = numbers.prefix(3)
90-
/// // [1, 2, 3]
91-
///
92-
/// The order of equal elements is not guaranteed to be preserved, and the
93-
/// order of the remaining elements is unspecified.
94-
///
95-
/// If you need to sort a collection but only need access to a prefix of its
96-
/// elements, using this method can give you a performance boost over sorting
97-
/// the entire collection.
98-
///
99-
/// - Parameter count: The k number of elements to partially sort.
100-
/// - Parameter areInIncreasingOrder: A predicate that returns true if its
101-
/// first argument should be ordered before its second argument;
102-
/// otherwise, false.
103-
///
104-
/// - Complexity: O(k log n)
105-
public mutating func partiallySort(
106-
_ count: Int,
107-
by areInIncreasingOrder: (Element, Element) throws -> Bool
108-
) rethrows {
109-
try __partiallySort(count, by: areInIncreasingOrder)
110-
}
111-
}
112-
113-
extension MutableCollection
114-
where Self: RandomAccessCollection, Element: Comparable {
115-
/// Rearranges this collection such that the 0...k range contains the first
116-
/// k smallest elements in this collection.
117-
///
118-
/// This example partially sorts an array of integers to retrieve its three
119-
/// smallest values:
120-
///
121-
/// var numbers = [7,1,6,2,8,3,9]
122-
/// numbers.partiallySort(3)
123-
/// // [1, 2, 3, 9, 7, 6, 8]
124-
/// let smallestThree = numbers.prefix(3)
125-
/// // [1, 2, 3]
126-
///
127-
/// The order of equal elements is not guaranteed to be preserved, and the
128-
/// order of the remaining elements is unspecified.
129-
///
130-
/// If you need to sort a collection but only need access to a prefix of its
131-
/// elements, using this method can give you a performance boost over sorting
132-
/// the entire collection.
133-
///
134-
/// - Parameter count: The k number of elements to partially sort
135-
/// in ascending order.
136-
///
137-
/// - Complexity: O(k log n)
138-
public mutating func partiallySort(_ count: Int) {
139-
partiallySort(count, by: <)
140-
}
141-
}
14210

14311
//===----------------------------------------------------------------------===//
144-
// __partiallySort(_:by:)
12+
// sortedPrefix(_:by:)
14513
//===----------------------------------------------------------------------===//
14614

147-
extension MutableCollection where Self: RandomAccessCollection {
148-
typealias Priority = (Element, Element) throws -> Bool
15+
extension Collection {
14916

150-
/// Partially sorts this collection by using an in-place heapsort that stops
151-
/// after we find the desired k amount of elements. The heap is stored and
152-
/// processed in reverse order so that the collection doesn't have to be
153-
/// flipped once the final result is found.
154-
///
155-
/// Complexity: O(k log n)
156-
mutating func __partiallySort(
157-
_ k: Int,
158-
by areInIncreasingOrder: Priority
159-
) rethrows {
160-
assert(k >= 0, """
161-
Cannot partially sort with a negative amount of elements!
17+
public func sortedPrefix(
18+
_ count: Int,
19+
by areInIncreasingOrder: (Element, Element) throws -> Bool
20+
) rethrows -> [Self.Element] {
21+
assert(count >= 0, """
22+
Cannot prefix with a negative amount of elements!
16223
"""
16324
)
164-
assert(k <= count, """
165-
Cannot partially sort more than this Sequence's size!
25+
assert(count <= self.count, """
26+
Cannot prefix more than this Collection's size!
16627
"""
16728
)
16829

169-
guard isEmpty == false && k > 0 else {
170-
return
171-
}
172-
173-
var heapEndIndex = 0
174-
for i in (count / 2)..<count {
175-
try siftDown(i, by: areInIncreasingOrder, heapEndIndex: heapEndIndex)
176-
}
177-
178-
swapAt(
179-
index(before: endIndex),
180-
index(startIndex, offsetBy: heapEndIndex)
181-
)
182-
heapEndIndex += 1
183-
184-
for i in 1..<k {
185-
try siftDown(
186-
count - 1,
187-
by: areInIncreasingOrder,
188-
heapEndIndex: heapEndIndex
189-
)
190-
swapAt(
191-
index(before: endIndex),
192-
index(startIndex, offsetBy: heapEndIndex)
193-
)
194-
heapEndIndex += 1
30+
// If we're attempting to prefix more than 10% of the collection, it's faster to sort everything.
31+
guard count < (self.count / 10) else {
32+
return Array(try sorted(by: areInIncreasingOrder).prefix(count))
19533
}
196-
}
19734

198-
/// Sifts down an element from this heap.
199-
/// The heap is stored in reverse order, so sifting down will actually
200-
/// move the element up in the heap.
201-
///
202-
/// - Parameter i: The element index to sift down.
203-
/// - Parameter by: The predicate to use when determining the priority
204-
/// of elements in the heap.
205-
/// - Parameter heapEndIndex: The index, in reverse order,
206-
/// where the heap ends.
207-
private mutating func siftDown(
208-
_ i: Int,
209-
by priority: Priority,
210-
heapEndIndex: Int
211-
) rethrows {
212-
let indexToSwap = try highestPriorityIndex(
213-
of: i,
214-
by: priority,
215-
heapEndIndex: heapEndIndex
216-
)
217-
guard indexToSwap != i else {
218-
return
35+
var result = try self.prefix(count).sorted(by: areInIncreasingOrder)
36+
for e in self.dropFirst(count) {
37+
if let last = result.last, try areInIncreasingOrder(last, e) { continue }
38+
if let insertionIndex = try result.firstIndex (where: { try areInIncreasingOrder(e, $0) }) {
39+
result.insert(e, at: insertionIndex)
40+
result.removeLast()
41+
}
21942
}
220-
swapAt(
221-
index(startIndex, offsetBy: i),
222-
index(startIndex, offsetBy: indexToSwap)
223-
)
224-
try siftDown(indexToSwap, by: priority, heapEndIndex: heapEndIndex)
225-
}
226-
227-
private func highestPriorityIndex(
228-
of index: Int,
229-
by priority: Priority,
230-
heapEndIndex: Int
231-
) rethrows -> Int {
232-
let reverseHeapTrueIndex = self.count - 1 - index
233-
let leftChildDistance =
234-
leftChildIndex(of: reverseHeapTrueIndex) - reverseHeapTrueIndex
235-
let leftChild = index - leftChildDistance
236-
237-
let rightChildDistance =
238-
rightChildIndex(of: reverseHeapTrueIndex) - reverseHeapTrueIndex
239-
let rightChild = index - rightChildDistance
240-
241-
let left = try highestPriorityIndex(
242-
of: index,
243-
and: leftChild,
244-
by: priority,
245-
heapEndIndex: heapEndIndex
246-
)
247-
248-
let right = try highestPriorityIndex(
249-
of: index,
250-
and: rightChild,
251-
by: priority,
252-
heapEndIndex: heapEndIndex
253-
)
254-
return try highestPriorityIndex(
255-
of: left,
256-
and: right,
257-
by: priority,
258-
heapEndIndex: heapEndIndex
259-
)
260-
}
261-
262-
private func leftChildIndex(of index: Int) -> Int {
263-
return (2 * index) + 1
43+
return result
26444
}
45+
}
26546

266-
private func rightChildIndex(of index: Int) -> Int {
267-
return (2 * index) + 2
268-
}
47+
extension Collection where Element: Comparable {
26948

270-
private func highestPriorityIndex(
271-
of parent: Int,
272-
and child: Int,
273-
by priority: Priority,
274-
heapEndIndex: Int
275-
) rethrows -> Int {
276-
guard child >= heapEndIndex else {
277-
return parent
278-
}
279-
let childElement = self[index(startIndex, offsetBy: child)]
280-
let parentElement = self[index(startIndex, offsetBy: parent)]
281-
guard try priority(childElement, parentElement) else {
282-
return parent
283-
}
284-
return child
49+
public func sortedPrefix(_ count: Int) -> [Element] {
50+
return sortedPrefix(count, by: <)
28551
}
28652
}

0 commit comments

Comments
 (0)