Question or problem with Swift language programming:
I realize the Swift book provided an implementation of a random number generator. Is the best practice to copy and paste this implementation in one’s own program? Or is there a library that does this that we can use now?
How to solve the problem:
Solution 1:
Swift 4.2+
Swift 4.2 shipped with Xcode 10 introduces new easy-to-use random functions for many data types.
You can call the random()
method on numeric types.
let randomInt = Int.random(in: 0..<6) let randomDouble = Double.random(in: 2.71828...3.14159) let randomBool = Bool.random()
Solution 2:
Use arc4random_uniform(n)
for a random integer between 0 and n-1.
let diceRoll = Int(arc4random_uniform(6) + 1)
Cast the result to Int so you don't have to explicitly type your vars as UInt32
(which seems un-Swifty).
Solution 3:
Edit: Updated for Swift 3.0
arc4random
works well in Swift, but the base functions are limited to 32-bit integer types (Int
is 64-bit on iPhone 5S and modern Macs). Here's a generic function for a random number of a type expressible by an integer literal:
public func arc4random(_ type: T.Type) -> T { var r: T = 0 arc4random_buf(&r, MemoryLayout.size) return r }
We can use this new generic function to extend UInt64
, adding boundary arguments and mitigating modulo bias. (This is lifted straight from arc4random.c)
public extension UInt64 { public static func random(lower: UInt64 = min, upper: UInt64 = max) -> UInt64 { var m: UInt64 let u = upper - lower var r = arc4random(UInt64.self) if u > UInt64(Int64.max) { m = 1 + ~u } else { m = ((max - (u * 2)) + 1) % u } while r < m { r = arc4random(UInt64.self) } return (r % u) + lower } }
With that we can extend Int64
for the same arguments, dealing with overflow:
public extension Int64 { public static func random(lower: Int64 = min, upper: Int64 = max) -> Int64 { let (s, overflow) = Int64.subtractWithOverflow(upper, lower) let u = overflow ? UInt64.max - UInt64(~s) : UInt64(s) let r = UInt64.random(upper: u) if r > UInt64(Int64.max) { return Int64(r - (UInt64(~lower) + 1)) } else { return Int64(r) + lower } } }
To complete the family...
private let _wordSize = __WORDSIZE public extension UInt32 { public static func random(lower: UInt32 = min, upper: UInt32 = max) -> UInt32 { return arc4random_uniform(upper - lower) + lower } } public extension Int32 { public static func random(lower: Int32 = min, upper: Int32 = max) -> Int32 { let r = arc4random_uniform(UInt32(Int64(upper) - Int64(lower))) return Int32(Int64(r) + Int64(lower)) } } public extension UInt { public static func random(lower: UInt = min, upper: UInt = max) -> UInt { switch (_wordSize) { case 32: return UInt(UInt32.random(UInt32(lower), upper: UInt32(upper))) case 64: return UInt(UInt64.random(UInt64(lower), upper: UInt64(upper))) default: return lower } } } public extension Int { public static func random(lower: Int = min, upper: Int = max) -> Int { switch (_wordSize) { case 32: return Int(Int32.random(Int32(lower), upper: Int32(upper))) case 64: return Int(Int64.random(Int64(lower), upper: Int64(upper))) default: return lower } } }
After all that, we can finally do something like this:
let diceRoll = UInt64.random(lower: 1, upper: 7)
Solution 4:
Edit for Swift 4.2
Starting in Swift 4.2, instead of using the imported C function arc4random_uniform(), you can now use Swift’s own native functions.
// Generates integers starting with 0 up to, and including, 10 Int.random(in: 0 ... 10)
You can use random(in:)
to get random values for other primitive values as well; such as Int, Double, Float and even Bool.
Swift versions < 4.2
This method will generate a random Int
value between the given minimum and maximum
func randomInt(min: Int, max: Int) -> Int { return min + Int(arc4random_uniform(UInt32(max - min + 1))) }
Solution 5:
I used this code:
var k: Int = random() % 10;