Swift:64位整数的随机数?

所以,在我目前的项目中,我需要使用64位整数,我需要抓取高达1000亿的随机数。 arc4random()/ arc4random_uniform()仅适用于无符号的32位整数。

我可能会稍微调整一下,因为每次通话的最小/最大范围可能不会超过20亿,但是如果我决定这样做,我希望能够保证自己的未来,我需要更广泛的范围。

有什么build议?

arc4random_buf()你可以创build“任意大”的随机数,所以这将是一个可能的解决scheme:

 // Swift 2: func random64(upper_bound: UInt64) -> UInt64 { // Generate 64-bit random number: var rnd : UInt64 = 0 arc4random_buf(&rnd, sizeofValue(rnd)) return rnd % upper_bound } // Swift 3: func random64(upper_bound: UInt64) -> UInt64 { // Generate 64-bit random number: var rnd : UInt64 = 0 arc4random_buf(&rnd, MemoryLayout.size(ofValue: rnd)) return rnd % upper_bound } 

当上界不是2的幂时,这个方法就会遇到“模偏差”的问题(请看为什么人们说在使用随机数发生器时存在模偏差 )。 在这里我已经从上面的线索翻译了答案https://stackoverflow.com/a/10989061/1187415 Swift:

 // Swift 2: func random64(upper_bound: UInt64) -> UInt64 { // Generate 64-bit random value in a range that is // divisible by upper_bound: let range = UInt64.max - UInt64.max % upper_bound var rnd : UInt64 = 0 repeat { arc4random_buf(&rnd, sizeofValue(rnd)) } while rnd >= range return rnd % upper_bound } // Swift 3: func random64(upper_bound: UInt64) -> UInt64 { // Generate 64-bit random value in a range that is // divisible by upper_bound: let range = UInt64.max - UInt64.max % upper_bound var rnd : UInt64 = 0 repeat { arc4random_buf(&rnd, MemoryLayout.size(ofValue: rnd)) } while rnd >= range return rnd % upper_bound } 

(乍一看,看起来好像循环可能不会终止,但是可以certificate,平均需要less于2次迭代。)

也许你可以把它组合成32位整数:

 var random64 = Int64(arc4random()) + (Int64(arc4random()) << 32) 

这里是我通常包括在我的项目中的一些帮手。 注意UInt64有界的帮助程序,除了检查范围小于UInt32.max的常见情况并且只对arc4random()执行一次调用之外,它与Martin R的答案基本相同。

 extension UInt32 { /// Returns a random number in `0...UInt32.max` static func random() -> UInt32 { return arc4random() } /// Returns a random number in `0..<upperBound` static func random(_ upperBound: UInt32) -> UInt32 { return arc4random_uniform(upperBound) } } extension UInt64 { private static func randomLowerHalf() -> UInt64 { return UInt64(UInt32.random()) } private static func randomLowerHalf(_ upperBound: UInt32) -> UInt64 { return UInt64(UInt32.random(upperBound)) } static func random() -> UInt64 { return (randomLowerHalf() << 32) &+ randomLowerHalf() } static func random(_ upperBound: UInt64) -> UInt64 { if let upperBound = UInt32(exactly: upperBound) { return randomLowerHalf(upperBound) } else if UInt64(UInt32.max) == upperBound { return randomLowerHalf() } else { var result: UInt64 repeat { result = random() } while result >= upperBound return result } } } extension Int32 { static func random() -> Int32 { return Int32(bitPattern: UInt32.random()) } static func random(_ upperBound: Int32) -> Int32 { assert(0 < upperBound, "upperBound(\(upperBound)) must be greater than 0") return Int32(bitPattern: UInt32.random(UInt32(bitPattern: upperBound))) } } extension Int64 { static func random() -> Int64 { return Int64(bitPattern: UInt64.random()) } static func random(_ upperBound: Int64) -> Int64 { assert(0 < upperBound, "upperBound(\(upperBound)) must be greater than 0") return Int64(bitPattern: UInt64.random(UInt64(bitPattern: upperBound))) } } extension Int { static func random() -> Int { return Int(IntMax.random()) } static func random(_ upperBound: Int) -> Int { assert(0 < upperBound, "upperBound(\(upperBound)) must be greater than 0") return Int(IntMax.random(IntMax(upperBound))) } } 

这是一个很好的解决scheme! (无论如何,因为我刚刚做出来了)

 let hex = UUID().uuidString.components(separatedBy: "-").suffix(2).joined() let rand = UInt64(hex, radix: 0x10) 

用Swift REPL进行快速testing:

https://repl.it/GeIs/0

 for _ in 0..<5_000_000 { let hex = UUID().uuidString.components(separatedBy: "-").suffix(2).joined() set.insert(UInt64(hex, radix: 0x10)!) } set.count // prints 5_000_000 

作为一个扩展…

 import Foundation extension UInt64 { static var random: UInt64 { let hex = UUID().uuidString .components(separatedBy: "-") .suffix(2) .joined() return UInt64(hex, radix: 0x10)! } }