How can I generate large, ranged random numbers in Swift?
我正在寻找一种有效的方法来在Swift中生成具有任意范围(甚至可能是
我已经看到的所有现有问题要么因大值(
这是
这些类型的全部范围。它被写为扩展方法
(现已针对Swift 2进行了更新),但是对于全局函数也可以做到这一点。
请注意,
如果
适用于所有OS X计算机和所有较新的iOS设备)。
对于
(这只是https://stackoverflow.com/a/10989061/1187415的Swift翻译)。
范围覆盖整个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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 // https://stackoverflow.com/a/26550169/1187415, https://stackoverflow.com/a/10989061/1187415 // 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 } } } |
例子:
1 2 | let u1 = UInt.random(minValue: 1000, maxValue: 2000) let u2 = UInt.random(minValue: UInt.min, maxValue: UInt.max) |
使用可以将有符号整数的情况减少为无符号情况。
溢出运算符和
1 2 3 4 5 6 7 8 9 10 11 12 | 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) } } |
例子:
1 2 | let i1 = Int.random(minValue: -1000, maxValue: 1000) let i2 = Int.random(minValue: Int.min, maxValue: Int.max) |
最后,
1 2 3 4 5 6 7 8 9 10 11 | 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) } } |
例:
1 | let d = Double.random(minValue: 10.5, maxValue: 123.5) |
Swift 3更新:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 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 // https://stackoverflow.com/a/26550169/1187415, https://stackoverflow.com/a/10989061/1187415 // 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) } } |