ChatGPT解决这个技术问题 Extra ChatGPT

How does one generate a random number in Swift?

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?


C
Catfish_Man

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()

I didn't have to explicitly import Darwin
In my Playground file I needed to import Darwin because I wasn't importing anything else.
SoliQuiD: except omit the extra underscore after arc4, i.e. arc4random_uniform(5).
Warning: RC4 or arc4 has been shown to be distinguishable from pure random values. So although arc4 (a stream cipher) sounds cryptographically secure, it actually isn't.
@MaartenBodewes: no longer directly relevant to this answer, but arc4random does not actually use the RC4 cipher, despite its name, on macOS or the BSDs. It uses a system-provided CSPRNG on macOS, and ChaCha20 on most BSDs. Swift's default RNG (as used in this answer) calls it as an implementation detail on macOS, but uses an appropriate underlying generator on each supported platform.
T
Tamás Sengel

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).


Very simple. I like it. Upvote! But an actual dice doesn't have 0. In your code, diceRoll could be 0. Just saying...
Yeah, you really want Int(arc4random_uniform(6)+1).
probability = Int(arc4random_uniform(UInt32(total))) – i also had to cast into UInt32
let randomElementInArray = Int(arc4random_uniform(array.count))
Don't forget to cast the parameter, n, entered into arc3random_uniform(n) to a UInt32(n) if you are using a value that isn't already of that type.
j
jstn

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<T: ExpressibleByIntegerLiteral>(_ type: T.Type) -> T {
    var r: T = 0
    arc4random_buf(&r, MemoryLayout<T>.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)

It does not compile: var r = arc4random(UInt64). Please advice what did you mean here?
@Ossir compiles fine for me... it means call the function arc4random (defined in the first code block) with the argument UInt64 which is a Type.
Does arc4random_buf (and therefore all of the 64-bit extensions) suffer from modulo bias?
Modulo bias only comes into play when you add an upper bound, so it doesn't apply to arc4random_buf. The purpose of these extensions is to do exactly what arc4random_uniform does (mitigate modulo bias) except for 64-bit types.
when using the float functions, how can I include the upper value in the range of possibilities? So lets say I do 0.0 as the lower and 1.0 as the upper. With this logic it will give me 0.0 up to 0.99999999. But instead I'd like to include the 1.0 as a possibility. How can I achieve this?
G
Groot

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)))
}

J
János

I used this code:

var k: Int = random() % 10;

you must call srandom(UInt32(time(nil))) first, otherwise it will always return the same number sequence
I read the apple doc on random() twice but could not glean its usage... I wish they simply included a simply code sample like this on above. "The random() function uses a non-linear, additive feedback, random number generator, employing a default table of size 31 long integers. It returns successive pseudo-random numbers in the range from 0 to (231)-1. The period of this random number generator is very large, approximately 16*((231)-1)." ... Thanks a lot apple... I'll be sure to reference this in my next thesis.
random() sometimes leads to a sudden crash on iPads. If this happens, use the arc4random_uniform(6) from above. If you use random() then you can create more random values by prepending srandomdev().
I got compiler error message: random is unavailable in Swift: Use arc4random instead.
This solution has modulo bias: zuttobenkyou.wordpress.com/2012/10/18/…
T
TwoStraws

As of iOS 9, you can use the new GameplayKit classes to generate random numbers in a variety of ways.

You have four source types to choose from: a general random source (unnamed, down to the system to choose what it does), linear congruential, ARC4 and Mersenne Twister. These can generate random ints, floats and bools.

At the simplest level, you can generate a random number from the system's built-in random source like this:

GKRandomSource.sharedRandom().nextInt()

That generates a number between -2,147,483,648 and 2,147,483,647. If you want a number between 0 and an upper bound (exclusive) you'd use this:

GKRandomSource.sharedRandom().nextIntWithUpperBound(6)

GameplayKit has some convenience constructors built in to work with dice. For example, you can roll a six-sided die like this:

let d6 = GKRandomDistribution.d6()
d6.nextInt()

Plus you can shape the random distribution by using things like GKShuffledDistribution. That takes a little more explaining, but if you're interested you can read my tutorial on GameplayKit random numbers.


Thanks for this tip, it is one of best answers. To use these functions one needs to add import GameplayKit. Swift 3 changed the syntax to GKRandomSource.sharedRandom().nextInt(upperBound: 6)
how heavy is this kit to import? i don't want to bloat my code.
D
Dave DeLong

You can do it the same way that you would in C:

let randomNumber = arc4random()

randomNumber is inferred to be of type UInt32 (a 32-bit unsigned integer)


Addendum: rand, arc4random, drand48 and friends are all in the Darwin module. It's already imported for you if you're building a Cocoa, UIKit, or Foundation app, but you'll need to import Darwin in playgrounds.
And don't attempt to cast the result of arc4random() to an Int — this will work fine on a 64-bit platform, but on a 32-bit platform, Ints are 32-bit signed, so you'll get unexpected negative numbers. This has tripped a few people up already, so I figured I'd mention it here.
C
Community

Use arc4random_uniform()

Usage:

arc4random_uniform(someNumber: UInt32) -> UInt32

This gives you random integers in the range 0 to someNumber - 1.

The maximum value for UInt32 is 4,294,967,295 (that is, 2^32 - 1).

Examples:

Coin flip let flip = arc4random_uniform(2) // 0 or 1

Dice roll let roll = arc4random_uniform(6) + 1 // 1...6

Random day in October let day = arc4random_uniform(31) + 1 // 1...31

Random year in the 1990s let year = 1990 + arc4random_uniform(10)

General form:

let number = min + arc4random_uniform(max - min + 1)

where number, max, and min are UInt32.

What about...

arc4random()

You can also get a random number by using arc4random(), which produces a UInt32 between 0 and 2^32-1. Thus to get a random number between 0 and x-1, you can divide it by x and take the remainder. Or in other words, use the Remainder Operator (%):

let number = arc4random() % 5 // 0...4

However, this produces the slight modulo bias (see also here and here), so that is why arc4random_uniform() is recommended.

Converting to and from Int

Normally it would be fine to do something like this in order to convert back and forth between Int and UInt32:

let number: Int = 10
let random = Int(arc4random_uniform(UInt32(number)))

The problem, though, is that Int has a range of -2,147,483,648...2,147,483,647 on 32 bit systems and a range of -9,223,372,036,854,775,808...9,223,372,036,854,775,807 on 64 bit systems. Compare this to the UInt32 range of 0...4,294,967,295. The U of UInt32 means unsigned.

Consider the following errors:

UInt32(-1) // negative numbers cause integer overflow error
UInt32(4294967296) // numbers greater than 4,294,967,295 cause integer overflow error

So you just need to be sure that your input parameters are within the UInt32 range and that you don't need an output that is outside of that range either.


R
R.S

Example for random number in between 10 (0-9);

import UIKit

let randomNumber = Int(arc4random_uniform(10))

Very easy code - simple and short.


B
Bhavin Bhadani

I've been able to just use rand() to get a random CInt. You can make it an Int by using something like this:

let myVar: Int = Int(rand())

You can use your favourite C random function, and just convert to value to Int if needed.


Yep, type conversion can be a tricky business otherwise and letting the Int constructor deal with it is real pain saver.
Note that if you're not calling srand(...) (call it once only) before using rand(), the sequence of numbers will always be exactly the same between each execution of your program. If you don't want this then use arc4random()
You could also used random(), which returns an Int rather than UInt32 -- and like @SomeGuy mentioned, simply call srandom(arc4random()) once anywhere before using it to ensure it has a different, randomized seed for each execution of your program.
Can anyone comment on rand() vs arc4random_uniform()?
C
Community

@jstn's answer is good, but a bit verbose. Swift is known as a protocol-oriented language, so we can achieve the same result without having to implement boilerplate code for every class in the integer family, by adding a default implementation for the protocol extension.

public extension ExpressibleByIntegerLiteral {
    public static func arc4random() -> Self {
        var r: Self = 0
        arc4random_buf(&r, MemoryLayout<Self>.size)
        return r
    }
}

Now we can do:

let i = Int.arc4random()
let j = UInt32.arc4random()

and all other integer classes are ok.


S
Sh_Khan

In Swift 4.2 you can generate random numbers by calling the random() method on whatever numeric type you want, providing the range you want to work with. For example, this generates a random number in the range 1 through 9, inclusive on both sides

let randInt = Int.random(in: 1..<10)

Also with other types

let randFloat = Float.random(in: 1..<20)
let randDouble = Double.random(in: 1...30)
let randCGFloat = CGFloat.random(in: 1...40)

A
Andy Jazz

Updated: June 09, 2022.

Swift 5.7

Let's assume we have an array:

let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

For iOS and macOS you can use system-wide random source in Xcode's framework GameKit. Here you can find GKRandomSource class with its sharedRandom() class method:

import GameKit

private func randomNumberGenerator() -> Int {
    let rand = GKRandomSource.sharedRandom().nextInt(upperBound: numbers.count)
    return numbers[rand]
}

randomNumberGenerator()

Also you can use a randomElement() method that returns a random element of a collection:

let randomNumber = numbers.randomElement()!
print(randomNumber)

Or use arc4random_uniform(). Pay attention that this method returns UInt32 type.

let generator = Int(arc4random_uniform(11))
print(generator)

And, of course, we can use a makeIterator() method that returns an iterator over the elements of the collection.

let iterator: Int = (1...10).makeIterator().shuffled().first!
print(iterator)

The final example you see here returns a random value within the specified range with a help of static func random(in range: ClosedRange<Int>) -> Int.

let randomizer = Int.random(in: 1...10)
print(randomizer)

Pseudo-random Double number generator drand48() returns a value between 0.0 and 1.0.

import Foundation

let randomInt = Int(drand48() * 10)

d
demiculus

Here is a library that does the job well https://github.com/thellimist/SwiftRandom

public extension Int {
    /// SwiftRandom extension
    public static func random(lower: Int = 0, _ upper: Int = 100) -> Int {
        return lower + Int(arc4random_uniform(UInt32(upper - lower + 1)))
    }
}

public extension Double {
    /// SwiftRandom extension
    public static func random(lower: Double = 0, _ upper: Double = 100) -> Double {
        return (Double(arc4random()) / 0xFFFFFFFF) * (upper - lower) + lower
    }
}

public extension Float {
    /// SwiftRandom extension
    public static func random(lower: Float = 0, _ upper: Float = 100) -> Float {
        return (Float(arc4random()) / 0xFFFFFFFF) * (upper - lower) + lower
    }
}

public extension CGFloat {
    /// SwiftRandom extension
    public static func random(lower: CGFloat = 0, _ upper: CGFloat = 1) -> CGFloat {
        return CGFloat(Float(arc4random()) / Float(UINT32_MAX)) * (upper - lower) + lower
    }
}

J
Jakub Truhlář

Since Swift 4.2

There is a new set of APIs:

let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomDouble = Double.random(in: 1 ... 10)

All numeric types now have the random(in:) method that takes range.

It returns a number uniformly distributed in that range.

TL;DR

Well, what is wrong with the "good" old way?

You have to use imported C APIs (They are different between platforms). And moreover...

What if I told you that the random is not that random?

If you use arc4random() (to calculate the remainder) like arc4random() % aNumber, the result is not uniformly distributed between the 0 and aNumber. There is a problem called the Modulo bias.

Modulo bias

Normally, the function generates a random number between 0 and MAX (depends on the type etc.). To make a quick, easy example, let's say the max number is 7 and you care about a random number in the range 0 ..< 2 (or the interval [0, 3) if you prefer that).

The probabilities for individual numbers are:

0: 3/8 = 37.5%

1: 3/8 = 37.5%

2: 2/8 = 25%

In other words, you are more likely to end up with 0 or 1 than 2. Of course, bare in mind that this is extremely simplified and the MAX number is much higher, making it more "fair".

This problem is addressed by SE-0202 - Random unification in Swift 4.2


R
Rajesh Sharma
 let MAX : UInt32 = 9
 let MIN : UInt32 = 1

    func randomNumber()
{
    var random_number = Int(arc4random_uniform(MAX) + MIN)
    print ("random = ", random_number);
}

J
Jeffrey Goldberg

I would like to add to existing answers that the random number generator example in the Swift book is a Linear Congruence Generator (LCG), it is a severely limited one and shouldn't be except for the must trivial examples, where quality of randomness doesn't matter at all. And a LCG should never be used for cryptographic purposes.

arc4random() is much better and can be used for most purposes, but again should not be used for cryptographic purposes.

If you want something that is guaranteed to be cryptographically secure, use SecCopyRandomBytes(). Note that if you build a random number generator into something, someone else might end up (mis)-using it for cryptographic purposes (such as password, key or salt generation), then you should consider using SecCopyRandomBytes() anyway, even if your need doesn't quite require that.


i
iSrinivasan27

Swift 4.2

Bye bye to import Foundation C lib arc4random_uniform()

// 1  
let digit = Int.random(in: 0..<10)

// 2
if let anotherDigit = (0..<10).randomElement() {
  print(anotherDigit)
} else {
  print("Empty range.")
}

// 3
let double = Double.random(in: 0..<1)
let float = Float.random(in: 0..<1)
let cgFloat = CGFloat.random(in: 0..<1)
let bool = Bool.random()

You use random(in:) to generate random digits from ranges. randomElement() returns nil if the range is empty, so you unwrap the returned Int? with if let. You use random(in:) to generate a random Double, Float or CGFloat and random() to return a random Bool.

More @ Official


C
Cœur
var randomNumber = Int(arc4random_uniform(UInt32(5)))

Here 5 will make sure that the random number is generated through zero to four. You can set the value accordingly.


If you pass 5 it will return 5 possible results from zero to four. 0...4
b
brokenrhino

Without arc4Random_uniform() in some versions of Xcode(in 7.1 it runs but doesn't autocomplete for me). You can do this instead.

To generate a random number from 0-5. First

import GameplayKit

Then

let diceRoll = GKRandomSource.sharedRandom().nextIntWithUpperBound(6)

Y
Yvo

The following code will produce a secure random number between 0 and 255:

extension UInt8 {
  public static var random: UInt8 {
    var number: UInt8 = 0
    _ = SecRandomCopyBytes(kSecRandomDefault, 1, &number)
    return number
  }
}

You call it like this:

print(UInt8.random)

For bigger numbers it becomes more complicated. This is the best I could come up with:

extension UInt16 {
  public static var random: UInt16 {
    let count = Int(UInt8.random % 2) + 1
    var numbers = [UInt8](repeating: 0, count: 2)
    _ = SecRandomCopyBytes(kSecRandomDefault, count, &numbers)
    return numbers.reversed().reduce(0) { $0 << 8 + UInt16($1) }
  }
}

extension UInt32 {
  public static var random: UInt32 {
    let count = Int(UInt8.random % 4) + 1
    var numbers = [UInt8](repeating: 0, count: 4)
    _ = SecRandomCopyBytes(kSecRandomDefault, count, &numbers)
    return numbers.reversed().reduce(0) { $0 << 8 + UInt32($1) }
  }
}

These methods use an extra random number to determine how many UInt8s are going to be used to create the random number. The last line converts the [UInt8] to UInt16 or UInt32.

I don't know if the last two still count as truly random, but you can tweak it to your likings :)


You cleverly avoided the bias introduced by modulo, +1 for that. You might warn readers why you did it.
That's interesting, I didn't really consider that modulo bias might be in play here. Perhaps the chances of getting a small number aren't the same as getting a large number.
S
Suhit Patil

Swift 4.2

Swift 4.2 has included a native and fairly full-featured random number API in the standard library. (Swift Evolution proposal SE-0202)

let intBetween0to9 = Int.random(in: 0...9) 
let doubleBetween0to1 = Double.random(in: 0...1)

All number types have the static random(in:) which takes the range and returns the random number in the given range


P
Politank-Z

You can use GeneratorOf like this:

var fibs = ArraySlice([1, 1])
var fibGenerator = GeneratorOf{
    _ -> Int? in
    fibs.append(fibs.reduce(0, combine:+))
    return fibs.removeAtIndex(0)
}

println(fibGenerator.next())
println(fibGenerator.next())
println(fibGenerator.next())
println(fibGenerator.next())
println(fibGenerator.next())
println(fibGenerator.next())

How's Fibonacci random?
Hi Nikolai, This code block is old version Swift 1.2. If you try new Swift 2.0. It wouldn't be work.
I understand, but still it looks like a fibonacci generator to me, not random numbers, like asked for in the question.
T
Tim Malone

I use this code to generate a random number:

//
//  FactModel.swift
//  Collection
//
//  Created by Ahmadreza Shamimi on 6/11/16.
//  Copyright © 2016 Ahmadreza Shamimi. All rights reserved.
//

import GameKit

struct FactModel {

    let fun  = ["I love swift","My name is Ahmadreza","I love coding" ,"I love PHP","My name is ALireza","I love Coding too"]


    func getRandomNumber() -> String {

        let randomNumber  = GKRandomSource.sharedRandom().nextIntWithUpperBound(fun.count)

        return fun[randomNumber]
    }
}

Welcome to SO. Code only answers are discouraged - please edit your answer to explain why this code answers the question, and how it works. See stackoverflow.com/help/how-to-answer for more information.
Please provide some context around your answer, and welcome to stackoverflow. :)
V
Vasily Bodnarchuk

Details

xCode 9.1, Swift 4

Math oriented solution (1)

import Foundation

class Random {

    subscript<T>(_ min: T, _ max: T) -> T where T : BinaryInteger {
        get {
            return rand(min-1, max+1)
        }
    }
}

let rand = Random()

func rand<T>(_ min: T, _ max: T) -> T where T : BinaryInteger {
    let _min = min + 1
    let difference = max - _min
    return T(arc4random_uniform(UInt32(difference))) + _min
}

Usage of solution (1)

let x = rand(-5, 5)       // x = [-4, -3, -2, -1, 0, 1, 2, 3, 4]
let x = rand[0, 10]       // x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Programmers oriented solution (2)

Do not forget to add Math oriented solution (1) code here

import Foundation

extension CountableRange where Bound : BinaryInteger {

    var random: Bound {
        return rand(lowerBound-1, upperBound)
    }
}

extension CountableClosedRange where Bound : BinaryInteger {

    var random: Bound {
        return rand[lowerBound, upperBound]
    }
}

Usage of solution (2)

let x = (-8..<2).random           // x = [-8, -7, -6, -5, -4, -3, -2, -1, 0, 1]
let x = (0..<10).random           // x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
let x = (-10 ... -2).random       // x = [-10, -9, -8, -7, -6, -5, -4, -3, -2]

Full Sample

Do not forget to add solution (1) and solution (2) codes here

private func generateRandNums(closure:()->(Int)) {

    var allNums = Set<Int>()
    for _ in 0..<100 {
        allNums.insert(closure())
    }
    print(allNums.sorted{ $0 < $1 })
}

generateRandNums {
    (-8..<2).random
}

generateRandNums {
    (0..<10).random
}

generateRandNums {
    (-10 ... -2).random
}

generateRandNums {
    rand(-5, 5)
}
generateRandNums {
    rand[0, 10]
}

Sample result

https://i.stack.imgur.com/AlR58.png


This answer is off topic. The question was how to generate a random number. Not how to make a random number library. Sheesh.