因为我的当前项目需要使用64位整数,并且我需要在高达1000亿的范围内获取随机数字,而arc4random()/arc4random_uniform()仅适用于无符号32位整数。
可能我可以做一些小调整,因为每次调用我的最小/最大范围可能不会超过20亿,但是我想要未雨绸缪以防我决定需要更广泛的范围。
有什么建议吗?
因为我的当前项目需要使用64位整数,并且我需要在高达1000亿的范围内获取随机数字,而arc4random()/arc4random_uniform()仅适用于无符号32位整数。
可能我可以做一些小调整,因为每次调用我的最小/最大范围可能不会超过20亿,但是我想要未雨绸缪以防我决定需要更广泛的范围。
有什么建议吗?
更新: 自 Swift 4.2 (随 Xcode 10.1 分发) 起,Swift 标准库中有一个统一的随机 API,请参见
您可以直接调用它。
UInt64.random(in: minValue ... maxValue)
在给定的范围内获取一个随机数。
(Swift 4.2 以前版本的答案:) 使用 arc4random_buf()
,您可以创建“任意大”的随机数,因此这是可能的解决方案:
// 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://dev59.com/12gu5IYBdhLWcg3w6bKo#10989061翻译成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
}
乍一看,循环似乎可能不会终止,但可以证明平均不到2次迭代就足够了。
也许你可以由两个32位整数组成它:
var random64 = Int64(arc4random()) + (Int64(arc4random()) << 32)
&+
,例如Int64(arc4random()) &+ (Int64(arc4random()) << 32)
。+
比&+
更好吗? - Cœur&+
在这里更好。 :) - Cœur以下是我通常在项目中包含的一些辅助函数。请注意 UInt64 bounded helper 函数,它与 Martin R 的答案大致相同,不同之处在于该函数检查范围是否小于 UInt32.max,并且仅执行一次 arc4random() 调用。
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)))
}
}
+
,例如(randomLowerHalf() << 32) + randomLowerHalf()
。&+
比+
更好吗? - Cœur&+
的意思是没有溢出检查。在这种特定情况下使用是安全的,因为您知道不会发生溢出。 - Sasha Lopoukhinelet hex = UUID().uuidString.components(separatedBy: "-").suffix(2).joined()
let rand = UInt64(hex, radix: 0x10)
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)!
}
}
您可以使用 UInt64.random(in:) 和 UInt64.max API 生成一个随机的无符号 64 位数字:
UInt64.random(in: 0...UInt64.max)