How can I generate large, ranged random numbers in Swift?
Here is a possible solution for UInt
, Int
and Double
which works with
the full range of those types. It is written as extension methods
(now updated for Swift 2), but the same can be done with global functions.
Note that arc4random_uniform()
produces 32-bit numbers only, so that
can not be used if Int
/UInt
are 64-bit integers (which is the case
for all OS X machines and all newer iOS devices).
For UInt
we use the technique from https://stackoverflow.com/a/26550169/1187415
(which is just a Swift translation of https://stackoverflow.com/a/10989061/1187415).
The case where the range covers the full range of UInt
is treated separately.
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
}
}
}
Examples:
let u1 = UInt.random(minValue: 1000, maxValue: 2000)
let u2 = UInt.random(minValue: UInt.min, maxValue: UInt.max)
The case of signed integers can be reduced to the unsigned case using the
overflow operators and the bitPattern:
conversion:
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)
}
}
Examples:
let i1 = Int.random(minValue: -1000, maxValue: 1000)
let i2 = Int.random(minValue: Int.min, maxValue: Int.max)
Finally, a straight-forward implementation for 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)
}
}
Example:
let d = Double.random(minValue: 10.5, maxValue: 123.5)
Update for Swift 3:
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)
}
}
How does one generate a random number in Swift?
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()
How to generate different random number in a range in Swift?
A Set
is simpler, it guarantees unique values
var randomIndex = Set<Int>()
while randomIndex.count < 3 {
randomIndex.insert(Int.random(in: 1 ... 10))
}
Another approach is to create an array of the values, pick a random element and remove this element from the array
var randomValues = Set<Int>()
var valueArray = Array(1...10)
for _ in 0..<3 {
guard let randomElement = valueArray.randomElement() else { break }
randomValues.insert(randomElement)
valueArray.remove(at: valueArray.firstIndex(of: randomElement)!)
}
Regarding is there any way to optimize my approach the for
loop in your code is extremely expensive.
First of all the recommended syntax for an index based loop is
for i in 0 ..< generatedValue.count {
But actually you don't need the index so Fast Enumeration is better
for value in generatedValue {
if randomValue != value {
The most expensive part of the loop is that the entire array is always iterated even if the value was already found. At least add a break
statement to exit the loop
for value in generatedValue {
if randomValue != value {
randomIndex.append(randomValue)
c += 1
break
}
}
But there is still a more efficient syntax
if !generatedValue.contains(randomValue) {
randomIndex.append(randomValue)
c += 1
}
Generate list of unique random numbers in Swift from range
There are 2 problems here:
You don't generate enough numbers. You need to keep generating random numbers until your set is large enough:
func getRandomNumbers(maxNumber: Int, listSize: Int)-> [Int] {
var randomNumbers = Set<Int>()
while randomNumbers.count < listSize {
let randomNumber = Int(arc4random_uniform(UInt32(maxNumber+1)))
randomNumbers.insert(randomNumber)
}
return randomNumbers
}You're biasing your random numbers by putting them in the ordering Set chooses, which is highly predictable. You should append your numbers to an array (to keep them in order that they are generated), while still leveraging a set in parallel, for its fast deduplication:
func getRandomNumbers(maxNumber: Int, listSize: Int)-> [Int] {
precondition(listSize < maxNumber, "Cannot generate a list of \(listSize) unique numbers, if they all have to be less than \(maxNumber)")
var randomNumbers = (array: [Int](), set: Set<Int>())
while randomNumbers.set.count < listSize {
let randomNumber = Int(arc4random_uniform(UInt32(maxNumber+1)))
if randomNumbers.set.insert(randomNumber).inserted { // If the number is unique
randomNumbers.array.append(randomNumber) // then also add it to the arary
}
}
return randomNumbers.array
}
Generate Array of Unique Random Numbers within Inclusive Range
You could make your live much easier using a Set to store all random numbers until you reach the expected number of randoms:
func uniqueRandoms(numberOfRandoms: Int, minNum: Int, maxNum: UInt32) -> [Int] {
var uniqueNumbers = Set<Int>()
while uniqueNumbers.count < numberOfRandoms {
uniqueNumbers.insert(Int(arc4random_uniform(maxNum + 1)) + minNum)
}
return uniqueNumbers.shuffled()
}
print(uniqueRandoms(numberOfRandoms: 5, minNum: 0, maxNum: 10))
func uniqueRandoms(numberOfRandoms: Int, minNum: Int, maxNum: UInt32, blackList: Int?) -> [Int] {
var uniqueNumbers = Set<Int>()
while uniqueNumbers.count < numberOfRandoms {
uniqueNumbers.insert(Int(arc4random_uniform(maxNum + 1)) + minNum)
}
if let blackList = blackList {
if uniqueNumbers.contains(blackList) {
while uniqueNumbers.count < numberOfRandoms+1 {
uniqueNumbers.insert(Int(arc4random_uniform(maxNum + 1)) + minNum)
}
uniqueNumbers.remove(blackList)
}
}
return uniqueNumbers.shuffled()
}
uniqueRandoms(numberOfRandoms: 3, minNum: 0, maxNum: 10, blackList: 8) // [0, 10, 7]
How to generate a random number in a range (10...20) using Swift
Xcode 11 • Swift 5.1 or later
extension Range where Bound: FixedWidthInteger {
var random: Bound { .random(in: self) }
func random(_ n: Int) -> [Bound] { (0..<n).map { _ in random } }
}
extension ClosedRange where Bound: FixedWidthInteger {
var random: Bound { .random(in: self) }
func random(_ n: Int) -> [Bound] { (0..<n).map { _ in random } }
}
Note: For older Swift versions check the edit history
Usage:
(10...20).random // 16
(0...1).random(10) // [0, 1, 0, 0, 1, 1, 1, 1, 1, 0]
Generate random number of certain amount of digits
Here is some pseudocode that should do what you want.
generateRandomNumber(20)
func generateRandomNumber(int numDigits){
var place = 1
var finalNumber = 0;
for(int i = 0; i < numDigits; i++){
place *= 10
var randomNumber = arc4random_uniform(10)
finalNumber += randomNumber * place
}
return finalNumber
}
Its pretty simple. You generate 20 random numbers, and multiply them by the respective tens, hundredths, thousands... place that they should be on. This way you will guarantee a number of the correct size, but will randomly generate the number that will be used in each place.
Update
As said in the comments you will most likely get an overflow exception with a number this long, so you'll have to be creative in how you'd like to store the number (String, ect...) but I merely wanted to show you a simple way to generate a number with a guaranteed digit length. Also, given the current code there is a small chance your leading number could be 0 so you should protect against that as well.
Generating random values in Swift between two integer values
try this
let randomNumber = arc4random_uniform(40) + 10
println(randomNumber)
in general form
let lower : UInt32 = 10
let upper : UInt32 = 50
let randomNumber = arc4random_uniform(upper - lower) + lower
println(randomNumber)
Generating random numbers with Swift
let randomIntFrom0To10 = Int.random(in: 1..<10)
let randomFloat = Float.random(in: 0..<1)
// if you want to get a random element in an array
let greetings = ["hey", "hi", "hello", "hola"]
greetings.randomElement()
Generating a random number in Swift
It really depends on how much casting you want to avoid. You could simply wrap it in a function:
func random(max maxNumber: Int) -> Int {
return Int(arc4random_uniform(UInt32(maxNumber)))
}
So then you only have to do the ugly casting once. Everywhere you want a random number with a maximum number:
let r = random(max: _names.count)
let name: String = _names[r]
As a side note, since this is Swift, your properties don't need _
in front of them.
Related Topics
How to Set the Legacy Swift Versions for Each Pod in Podfile
Draw Scenekit Object Between Two Points
What Versions of Swift Are Supported by What Versions of Xcode
Uploading Image to Firebase Storage and Database
How to Save a Struct to Realm in Swift
Is Force Cast Really Bad and Should Always Avoid It
Uitextview Highlight All Matches Using Swift
How to Specify That a Non-Generic Swift Type Should Comply to a Protocol
Load Image from iOS 8 Framework
Embedding Videos in a Tableview Cell
Property Observers Willset and Didset; Property Getters and Setters
Generating Random Numbers With Swift
How to Document the Parameters of a Function'S Closure Parameter in Swift 3
How to Detect a Swiftui Touchdown Event with No Movement or Duration