While we’re still looking at the Fisher-Yates shuffle package, I stumbled upon this <https://github.com/apple/example-package-fisheryates/blob/master/Sources/FisherYates/random.swift#L18> definition of random():
public let random: (Int) -> Int = { while true { let x = Glibc.random() % $0 let y = Glibc.random() % $0 guard x == y else { return x } } } Perhaps I’m mistaken here, but if this is random(3) <https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/random.3.html> don’t we have to 1. seed it before use and 2. deal with modulo bias? I’m also not quite sure why the guard is there, either. Saagar Jha > On Dec 16, 2017, at 16:37, Daniel Dunbar via swift-users > <swift-users@swift.org> wrote: > > Would you like to post a PR to fix these issues? > > - Daniel > >> On Dec 16, 2017, at 4:34 PM, Nevin Brackett-Rozinsky via swift-users >> <swift-users@swift.org <mailto:swift-users@swift.org>> wrote: >> >> The example implementation of the Fisher-Yates shuffle found here >> <https://github.com/apple/example-package-fisheryates/blob/master/Sources/FisherYates/Fisher-Yates_Shuffle.swift> >> on Apple’s GitHub (and linked from swift.org/package-manager >> <https://swift.org/package-manager/>) has some problems. Stripping it down >> to just the Swift 4 version, the code is: >> >> public extension MutableCollection where Index == Int, IndexDistance == Int { >> mutating func shuffle() { >> guard count > 1 else { return } >> >> for i in 0..<count - 1 { >> let j = random(count - i) + i >> guard i != j else { continue } >> swapAt(i, j) >> } >> } >> } >> >> The main issues are: >> >> 1) It assumes that indices are 0-based. >> 2) It assumes that indices can be randomly accessed by addition. >> >> The former means it does not work for ArraySlice, and the latter means it >> won’t work for all custom types. Additionally, the “count” property (which >> is only guaranteed to be O(n) here) is accessed inside the loop, thus making >> the algorithm potentially O(n²). >> >> To fix everything, we’ll want RandomAccessCollection conformance. Once we >> add that, we no longer need “Index == Int”. The result looks like: >> >> public extension MutableCollection where Self: RandomAccessCollection, >> IndexDistance == Int { >> mutating func shuffle() { >> for n in 0 ..< count-1 { >> let i = index(startIndex, offsetBy: n) >> let j = index(i, offsetBy: random(count-n)) >> swapAt(i, j) >> } >> } >> } >> >> Both of the original guard statements would be superfluous here (notably, >> “swapAt” is documented to have no effect when i and j are the same) so I >> removed them. >> >> Technically we could get away without random access and still have an O(n) >> algorithm, at the cost of copying the indices to an array: >> >> public extension MutableCollection { >> mutating func shuffle() { >> guard !isEmpty else { return } >> >> var idx = Array(indices) >> >> for i in 0 ..< idx.count - 1 { >> let j = i + random(idx.count - i) >> swapAt(idx[i], idx[j]) >> idx.swapAt(i, j) >> } >> } >> } >> >> In any event, just in case someone was using a cut-and-paste version of the >> example code in their own project, now you know it needs adjustment. >> >> Nevin >> _______________________________________________ >> swift-users mailing list >> swift-users@swift.org <mailto:swift-users@swift.org> >> https://lists.swift.org/mailman/listinfo/swift-users > > _______________________________________________ > swift-users mailing list > swift-users@swift.org > https://lists.swift.org/mailman/listinfo/swift-users
_______________________________________________ swift-users mailing list swift-users@swift.org https://lists.swift.org/mailman/listinfo/swift-users