Вы можете поместить документы в сегменты:
var buckets = Array(repeating: [Document](), count: 11)
for i in documents.indices {
buckets[10 - documents[i].priority].append(documents[i])
}
Это наихудший случай алгоритма O ( n ) в отличие от O TimSort ( n log (*)1008 * n )).
, а затем перетасовать последний сегмент:
buckets[10].shuffle()
последний, но не менее важный, flatMap
массив buckets
:
let sorted = buckets.flatMap { $0 }
Speed Shuffle
Если вы хотите еще более быстрый перемешать, вы можете использовать этот updated Алгоритм Фишера-Йейтса (Быстрее, чем Array.shuffled()
):
extension Array where Element: Comparable {
mutating func fisherYatesShuffle() {
if count < 2 {
return
}
for i in 1..<count {
let ix = abs(x.next()) % (i+1)
swapAt(i,ix)
}
}
}
Или, в более общем случае, для MutableCollection
(который включает ArraySlice
):
extension MutableCollection where Index == Int, Element: Comparable {
mutating func fisherYatesShuffle() {
if count < 2 {
return
}
let start = self.index(after: startIndex)
for i in start..<endIndex {
let ix = abs(x.next()) % (i + 1 - startIndex) + startIndex
swapAt(i, ix)
}
}
}
Это расширение использует Генератор случайных чисел Xoshiro (Быстрее, чем Int.random(in:)
, но менее случайный / равномерный):
struct Xoshiro: RandomNumberGenerator {
public typealias StateType = (UInt32, UInt32, UInt32, UInt32)
private var state: StateType
public init(seed: StateType) {
self.state = seed
}
public mutating func next() -> Int {
let x = state.1 &* 5
let result = ((x &<< 7) | (x &>> 25)) &* 9
let t = state.1 &<< 9
state.2 ^= state.0
state.3 ^= state.1
state.1 ^= state.2
state.0 ^= state.3
state.2 ^= t
state.3 = (state.3 &<< 21) | (state.3 &>> 11)
return Int(result)
}
}
var x = Xoshiro(seed: (UInt32.random(in: 0..<10), //Other upper limits could be used to increase randomness
UInt32.random(in: 0..<10),
UInt32.random(in: 0..<10),
UInt32.random(in: 0..<10)))
Чтобы использовать его, Document
должно бытьComparable
:
struct Document: Comparable {
let priority: Int
static func < (lhs: Document, rhs: Document) -> Bool {
return lhs.priority < rhs.priority
}
}
и назовите нашу случайность так:
buckets[10].fisherYatesShuffle()
Разделение, сортировка, перемешивание
Согласно Джош ХоманнСовет , вы также можете разбить массив documents
, поместив документы с нулевым значением prирити после сводного индекса.Затем отсортируйте первый раздел и перетасуйте второй:
var result = documents
let pivot = result.partition { $0.priority == 0 }
result[..<pivot].sort(by: > )
result[pivot...].shuffle()
Тесты
Здесь - некоторые тесты:
Joakim Danielson's : 1643µs
MartinR's : 169µs
Josh Homann's : 152µs
Orig. Fisher-Yates : 38µs
This : 15µs
(Хотя TIO использует Swift 4, сравнительно сопоставимые результаты были получены при использовании того же кода на моей локальной машине с Swift 5, запущенного в терминале с оптимизацией)