Swift does not provide a feature to directly create an array of random numbers. Such an array must be built by generating a random number and assigning that number to an array element. The following sections demonstrate several methods to generate random numbers in Swift. Each method is benchmarked by filling a large array with random numbers.
Swift provides a static random method for different types such as Double.random
. This uses the system's default random number generator which is cryptographically secure whenever possible. The code below uses Double.random
to fill an array of 100,000,000 elements with random numbers that range from 0 to 1 (excluding 1).
// rand.swift
func main() {
let n = 100_000_000
let result = Array<Double>(unsafeUninitializedCapacity: n) { buffer, initCount in
for i in 0..<n {
buffer[i] = Double.random(in: 0..<1)
}
initCount = n
}
print(result[0])
print(result[n - 1])
}
main()
The Accelerate framework provides a drand48
function that returns a random double-precision value over a uniform distribution [0, 1). This function generates pseudo-random numbers using a linear congruential algorithm with 48-bit integer arithmetic. The code below is similar to the previous example except drand48
generates the random numbers for the array elements.
// rand48.swift
import Accelerate
func main() {
let n = 100_000_000
let result = Array<Double>(unsafeUninitializedCapacity: n) { buffer, initCount in
for i in 0..<n {
buffer[i] = drand48()
}
initCount = n
}
print(result[0])
print(result[n - 1])
}
main()
The Accelerate framework also provides access to LAPACK routines. The LAPACK dlarnv function creates an array of random numbers from a 0 to 1 uniform distribution when idist
is 1.
// lapack.swift
import Accelerate
func main() {
let n = 100_000_000
let result = Array<Double>(unsafeUninitializedCapacity: n) { buffer, initCount in
var idist: Int32 = 1
var nn = Int32(n)
var iseed: [Int32] = (0..<3).map { _ in Int32.random(in: 1..<4095) }
iseed += [2 * (Int32.random(in: 1..<4095) / 2) + 1 ]
dlarnv_(&idist, &iseed, &nn, buffer.baseAddress)
initCount = n
}
print(result[0])
print(result[n - 1])
}
main()
The static random methods like Double.random
accept a RandomNumberGenerator. The example below is a Swift implementation of the wyrand pseudorandom number generator which is associated with the wyhash function by Wang Yi. The generator is passed to the Double.random
method to create the random number for the array element. There is also the SwiftWyhash port by Daniel Lemire that is based on an older version of wyrand. Also see Daniel's 2019 article about fast random number generators.
// wyrand.swift
import Foundation
struct WyRand: RandomNumberGenerator {
private var state : UInt64
init(seed: UInt64 = mach_absolute_time()) {
state = seed
}
mutating func next() -> UInt64 {
state &+= 0x2d358dccaa6c78a5
let mul = state.multipliedFullWidth(by: state ^ 0x8bb84b93962eacc9)
return mul.low ^ mul.high
}
}
func main() {
let n = 100_000_000
var rng = WyRand()
let result = Array<Double>(unsafeUninitializedCapacity: n) { buffer, initCount in
for i in 0..<n {
buffer[i] = Double.random(in: 0..<1, using: &rng)
}
initCount = n
}
print(result[0])
print(result[n - 1])
}
main()
This example is similar to the WyRand code given above except Double.random
is not used. Instead of passing the generator to Double.random
, the integer returned from the generator is converted to a uniform distribution [0, 1) using the wy2u01
function. The function shown here is based on the wy2u01
C function given in the wyhash repo.
// wyuniform.swift
import Foundation
struct WyRand: RandomNumberGenerator {
private var state : UInt64
init(seed: UInt64 = mach_absolute_time()) {
state = seed
}
mutating func next() -> UInt64 {
state &+= 0x2d358dccaa6c78a5
let mul = state.multipliedFullWidth(by: state ^ 0x8bb84b93962eacc9)
return mul.low ^ mul.high
}
}
func wy2u01(_ r: UInt64) -> Double {
let norm = 1.0 / Double(1 << 52)
return Double(r >> 12) * norm
}
func main() {
let n = 100_000_000
var rng = WyRand()
let result = Array<Double>(unsafeUninitializedCapacity: n) { buffer, initCount in
for i in 0..<n {
let r = rng.next()
let uniform = wy2u01(r)
buffer[i] = uniform
}
initCount = n
}
print(result[0])
print(result[n - 1])
}
main()
The compiler and benchmark commands for each example are shown below. The hyperfine command-line tool is used to benchmark the compiled code.
# Compile the code
swiftc -Xcc -DACCELERATE_NEW_LAPACK -Ounchecked lapack.swift
swiftc -Ounchecked rand.swift
swiftc -Ounchecked rand48.swift
swiftc -Ounchecked wyrand.swift
swiftc -Ounchecked wyuniform.swift
# Run then benchmark
hyperfine --warmup 3 './lapack' './rand' './rand48' './wyrand' './wyuniform'
A summary of the benchmark results is shown in the table below. The Time column is the mean time in milliseconds. The Speedup column is how much faster WyRand uniform is compared to the other methods. The results were obtained on a 2025 M4 MacBook Air with 32 GB of unified memory and with Swift version 6.1.2. The WyRand uniform generator was 50x faster than the default random number generator in Swift when filling an array with 100,000,000 random numbers.
Method | Filename | Time (ms) | Speedup |
---|---|---|---|
Swift random | rand.swift | 3318 | 50.5x |
LAPACK dlarnv | lapack.swift | 407 | 6.2x |
Accelerate drand48 | rand48.swift | 185 | 2.8x |
WyRand random | wyrand.swift | 81 | 1.2x |
WyRand uniform | wyuniform.swift | 65 | - |
Based on the benchmarks, using Swift's default random number generator is very slow compared to pseudorandom number generators such as wyrand. If you don't need cryptographically secure random numbers then consider using a custom random number generator for faster performance; especially when filling arrays with pseudorandom numbers.
This post is based on a Swift Forums discussion. Thank you to everyone on the forum post who offered advice and comments on this topic. The code discussed in this post is available on GitHub in the swift-macos/scripts/random-array directory.
Swift Programming for macOS © 2025
Built with Genja by Gavin Wiggins