Gavin Wiggins


Array of Random Numbers in Swift

Written on May 29, 2025

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 random

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

Accelerate drand48

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

LAPACK dlarnv

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

WyRand random

The static random methods like Double.random accept a RandomNumberGenerator. The example below is a Swift implementation of the wyrand pseudo-random 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()

WyRand uniform

This example is similar to the WyRand code given above except Double.random is not used. The integer returned from the generator is converted to a double by dividing by UInt64.max + 1 which is 18446744073709551616. The result is a random number in the range [0, 1) including 0 but excluding 1.

// 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 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 = Double(r) / 18446744073709551616
            buffer[i] = uniform
        }
        initCount = n
    }

    print(result[0])
    print(result[n - 1])
}

main()

Benchmarks

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 pseudo-random 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 random numbers.

Gavin Wiggins © 2025.
Made on a Mac with Genja. Hosted on GitHub Pages.