我意识到Swift书籍提供了一个随机数生成器的实现。复制和粘贴这个实现是最佳实践吗?或者有没有这样的库,我们现在就可以使用?


当前回答

从iOS 9开始,你可以使用新的GameplayKit类以各种方式生成随机数。

你有四种源类型可以选择:一般随机源(未命名,直到系统选择它做什么),线性同余,ARC4和梅森扭扭。它们可以生成随机int型、浮点型和bool型。

在最简单的层面上,你可以从系统内置的随机源生成一个随机数,如下所示:

GKRandomSource.sharedRandom().nextInt()

这就产生了一个介于- 2147,483,648到2147,483,647之间的数字。如果你想要一个介于0和上界(不包含)之间的数字,你可以使用这个:

GKRandomSource.sharedRandom().nextIntWithUpperBound(6)

GameplayKit内置了一些方便的构造函数来处理骰子。例如,你可以像这样掷一个六面骰子:

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

此外,你还可以使用GKShuffledDistribution之类的东西来塑造随机分布。这需要更多解释,但如果你感兴趣,你可以阅读我关于GameplayKit随机数的教程。

其他回答

Swift 4 + 2。

Xcode 10附带的Swift 4.2为许多数据类型引入了新的易于使用的随机函数。

只需在数值类型上调用random()方法。

let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()

更新日期:2022年6月9日。

斯威夫特5.7

假设我们有一个数组:

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

对于iOS和macOS,你可以在Xcode的框架GameKit中使用系统范围内的随机源代码。在这里你可以找到GKRandomSource类及其sharerandom()类方法:

import GameKit

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

randomNumberGenerator()

你也可以使用randomElement()方法返回一个集合的随机元素:

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

或者使用arc4random_uniform()。注意,该方法返回UInt32类型。

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

当然,我们也可以使用makeIterator()方法,该方法返回集合元素上的迭代器。

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

你在这里看到的最后一个例子通过静态func random(in range: ClosedRange<Int>) -> Int在指定范围内返回一个随机值。

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

伪随机双位数生成器drand48()返回0.0到1.0之间的值。

import Foundation

let randomInt = Int(drand48() * 10)

从Swift 4.2开始

有一组新的api:

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

所有数字类型现在都有random(in:)方法,该方法接受range。 它返回一个在该范围内均匀分布的数字。


博士TL;

那么,“好”的旧方法有什么错呢?

你必须使用导入的C api(它们在不同平台之间是不同的)。 而且……

如果我告诉你随机并不是那么随机呢?

如果您使用arc4random()(计算余数),如arc4random() % aNumber,结果不是均匀分布在0和aNumber之间。有一个问题叫做模偏倚。

模的偏见

通常,该函数生成一个0到MAX之间的随机数(取决于类型等)。举个简单的例子,假设最大的数字是7,你关心的是一个范围为0 ..< 2(或者间隔[0,3),如果你喜欢的话)。

单个数字的概率为:

0: 3/8 = 37.5% 1: 3/8 = 37.5% 2: 2/8 = 25%

换句话说,你更有可能得到0或1而不是2。 当然,请记住,这是非常简化的,MAX数字要高得多,使其更加“公平”。

Swift 4.2中的SE-0202 -随机统一解决了这个问题

你可以像在C中那样做:

let randomNumber = arc4random()

推断randomNumber类型为UInt32(32位无符号整数)

细节

xCode 9.1, Swift 4

面向数学的解(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
}

溶液使用方法(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]

面向程序员的解决方案(2)

不要忘记在这里添加面向数学的解决方案(1)代码

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

溶液使用方法(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]

完整的样品

不要忘记在这里添加溶液(1)和溶液(2)的代码

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

样品结果