如何在 Swift 中生成大范围的随机数?
How can I generate large, ranged random numbers in Swift?
我正在寻找一种在 Swift 中生成大数(包括浮点类型!)的有效方法,范围任意(甚至可以是 UInt.max
或 Int.max
)
我见过的所有现有问题要么因大值 (UInt.max
) 而崩溃,要么不支持范围。我知道您可以从 /dev/urandom
中读取随机字节,但这无助于将这些值限制在给定的间隔内(而且我很确定循环直到它效率不高)。
这是 UInt
、Int
和 Double
的可能解决方案
这些类型的全部范围。它被写成扩展方法
(现在更新为 Swift 2),但同样可以用全局函数完成。
请注意 arc4random_uniform()
仅生成 32 位数字,因此
如果 Int
/UInt
是 64 位整数(就是这种情况
对于所有 OS X 机器和所有较新的 iOS 设备)。
对于UInt
,我们使用中的技术
(这只是 的 Swift 翻译)。
范围覆盖UInt
全范围的情况另行处理
extension UInt {
static func random(minValue minValue : UInt, maxValue : UInt) -> UInt {
precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")
if minValue == UInt.min && maxValue == UInt.max {
// Random number in the full range of UInt:
var rnd : UInt = 0
arc4random_buf(&rnd, sizeofValue(rnd))
return rnd
} else {
// Compute random number in the range 0 ... (maxValue-minValue),
// using the technique from
//
// and avoiding the "modulo bias problem":
let range = maxValue - minValue + 1
let randLimit = UInt.max - UInt.max % range
var rnd : UInt = 0
repeat {
arc4random_buf(&rnd, sizeofValue(rnd))
} while rnd >= randLimit
rnd = rnd % range
// Transform `rnd` back to the range minValue ... maxValue:
return minValue + rnd
}
}
}
示例:
let u1 = UInt.random(minValue: 1000, maxValue: 2000)
let u2 = UInt.random(minValue: UInt.min, maxValue: UInt.max)
可以使用
溢出运算符和 bitPattern:
转换:
extension Int {
static func random(minValue minValue : Int, maxValue : Int) -> Int {
precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")
// Compute unsigned random number in the range 0 ... (maxValue-minValue):
let diff = UInt(bitPattern: maxValue &- minValue)
let rnd = UInt.random(minValue: 0, maxValue: diff)
// Transform `rnd` back to the range minValue ... maxValue:
return minValue &+ Int(bitPattern: rnd)
}
}
示例:
let i1 = Int.random(minValue: -1000, maxValue: 1000)
let i2 = Int.random(minValue: Int.min, maxValue: Int.max)
最后,Double
的直接实现:
extension Double {
static func random(minValue minValue : Double, maxValue : Double) -> Double {
precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")
// Random floating point number in the range 0.0 ... 1.0:
let rnd = Double(UInt.random(minValue: 0, maxValue: UInt.max))/Double(UInt.max)
// Scale to range minValue ... maxValue:
return minValue + rnd * (maxValue - minValue)
}
}
示例:
let d = Double.random(minValue: 10.5, maxValue: 123.5)
Swift3 的更新:
extension UInt {
static func random(minValue: UInt, maxValue: UInt) -> UInt {
precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")
if minValue == UInt.min && maxValue == UInt.max {
// Random number in the full range of UInt:
var rnd: UInt = 0
arc4random_buf(&rnd, MemoryLayout.size(ofValue: rnd))
return rnd
} else {
// Compute random number in the range 0 ... (maxValue-minValue),
// using the technique from
//
// and avoiding the "modulo bias problem":
let range = maxValue - minValue + 1
let randLimit = UInt.max - UInt.max % range
var rnd: UInt = 0
repeat {
arc4random_buf(&rnd, MemoryLayout.size(ofValue: rnd))
} while rnd >= randLimit
rnd = rnd % range
// Transform `rnd` back to the range minValue ... maxValue:
return minValue + rnd
}
}
}
extension Int {
static func random(minValue: Int, maxValue: Int) -> Int {
precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")
// Compute unsigned random number in the range 0 ... (maxValue-minValue):
let diff = UInt(bitPattern: maxValue &- minValue)
let rnd = UInt.random(minValue: 0, maxValue: diff)
// Transform `rnd` back to the range minValue ... maxValue:
return minValue &+ Int(bitPattern: rnd)
}
}
extension Double {
static func random(minValue: Double, maxValue: Double) -> Double {
precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")
// Random floating point number in the range 0.0 ... 1.0:
let rnd = Double(UInt.random(minValue: 0, maxValue: UInt.max))/Double(UInt.max)
// Scale to range minValue ... maxValue:
return minValue + rnd * (maxValue - minValue)
}
}
我正在寻找一种在 Swift 中生成大数(包括浮点类型!)的有效方法,范围任意(甚至可以是 UInt.max
或 Int.max
)
我见过的所有现有问题要么因大值 (UInt.max
) 而崩溃,要么不支持范围。我知道您可以从 /dev/urandom
中读取随机字节,但这无助于将这些值限制在给定的间隔内(而且我很确定循环直到它效率不高)。
这是 UInt
、Int
和 Double
的可能解决方案
这些类型的全部范围。它被写成扩展方法
(现在更新为 Swift 2),但同样可以用全局函数完成。
请注意 arc4random_uniform()
仅生成 32 位数字,因此
如果 Int
/UInt
是 64 位整数(就是这种情况
对于所有 OS X 机器和所有较新的 iOS 设备)。
对于UInt
,我们使用中的技术
(这只是 的 Swift 翻译)。
范围覆盖UInt
全范围的情况另行处理
extension UInt {
static func random(minValue minValue : UInt, maxValue : UInt) -> UInt {
precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")
if minValue == UInt.min && maxValue == UInt.max {
// Random number in the full range of UInt:
var rnd : UInt = 0
arc4random_buf(&rnd, sizeofValue(rnd))
return rnd
} else {
// Compute random number in the range 0 ... (maxValue-minValue),
// using the technique from
//
// and avoiding the "modulo bias problem":
let range = maxValue - minValue + 1
let randLimit = UInt.max - UInt.max % range
var rnd : UInt = 0
repeat {
arc4random_buf(&rnd, sizeofValue(rnd))
} while rnd >= randLimit
rnd = rnd % range
// Transform `rnd` back to the range minValue ... maxValue:
return minValue + rnd
}
}
}
示例:
let u1 = UInt.random(minValue: 1000, maxValue: 2000)
let u2 = UInt.random(minValue: UInt.min, maxValue: UInt.max)
可以使用
溢出运算符和 bitPattern:
转换:
extension Int {
static func random(minValue minValue : Int, maxValue : Int) -> Int {
precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")
// Compute unsigned random number in the range 0 ... (maxValue-minValue):
let diff = UInt(bitPattern: maxValue &- minValue)
let rnd = UInt.random(minValue: 0, maxValue: diff)
// Transform `rnd` back to the range minValue ... maxValue:
return minValue &+ Int(bitPattern: rnd)
}
}
示例:
let i1 = Int.random(minValue: -1000, maxValue: 1000)
let i2 = Int.random(minValue: Int.min, maxValue: Int.max)
最后,Double
的直接实现:
extension Double {
static func random(minValue minValue : Double, maxValue : Double) -> Double {
precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")
// Random floating point number in the range 0.0 ... 1.0:
let rnd = Double(UInt.random(minValue: 0, maxValue: UInt.max))/Double(UInt.max)
// Scale to range minValue ... maxValue:
return minValue + rnd * (maxValue - minValue)
}
}
示例:
let d = Double.random(minValue: 10.5, maxValue: 123.5)
Swift3 的更新:
extension UInt {
static func random(minValue: UInt, maxValue: UInt) -> UInt {
precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")
if minValue == UInt.min && maxValue == UInt.max {
// Random number in the full range of UInt:
var rnd: UInt = 0
arc4random_buf(&rnd, MemoryLayout.size(ofValue: rnd))
return rnd
} else {
// Compute random number in the range 0 ... (maxValue-minValue),
// using the technique from
//
// and avoiding the "modulo bias problem":
let range = maxValue - minValue + 1
let randLimit = UInt.max - UInt.max % range
var rnd: UInt = 0
repeat {
arc4random_buf(&rnd, MemoryLayout.size(ofValue: rnd))
} while rnd >= randLimit
rnd = rnd % range
// Transform `rnd` back to the range minValue ... maxValue:
return minValue + rnd
}
}
}
extension Int {
static func random(minValue: Int, maxValue: Int) -> Int {
precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")
// Compute unsigned random number in the range 0 ... (maxValue-minValue):
let diff = UInt(bitPattern: maxValue &- minValue)
let rnd = UInt.random(minValue: 0, maxValue: diff)
// Transform `rnd` back to the range minValue ... maxValue:
return minValue &+ Int(bitPattern: rnd)
}
}
extension Double {
static func random(minValue: Double, maxValue: Double) -> Double {
precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")
// Random floating point number in the range 0.0 ... 1.0:
let rnd = Double(UInt.random(minValue: 0, maxValue: UInt.max))/Double(UInt.max)
// Scale to range minValue ... maxValue:
return minValue + rnd * (maxValue - minValue)
}
}