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


当前回答

使用arc4random_uniform ()

用法:

arc4random_uniform(someNumber: UInt32) -> UInt32

这将为您提供0到someNumber - 1范围内的随机整数。

UInt32的最大值为4,294,967,295(即2^32 - 1)。

例子:

抛硬币 让flip = arc4random_uniform(2) // 0或1 骰子滚 Let roll = arc4random_uniform(6) + 1 // 1 十月的任意一天 Let day = arc4random_uniform(31) + 1 // 1 90年代的任意一年 Let year = 1990 + arc4random_uniform(10)

一般形式:

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

其中number、max、min为UInt32。

是什么……

arc4random ()

你也可以通过使用arc4random()得到一个随机数,它会产生一个在0到2^32-1之间的UInt32。因此,要得到一个介于0和x-1之间的随机数,可以将其除以x并取余数。或者换句话说,使用余数运算符(%):

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

然而,这会产生轻微的模数偏差(参见这里和这里),所以这就是为什么建议使用arc4random_uniform()。

从Int转换到Int

通常情况下,为了在Int和UInt32之间来回转换,这样做是很好的:

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

但问题是,Int的范围是-2,147,483,648…2,147,483,647在32位系统和范围-9,223,372,036,854,775,808…9223,372,036,854,775,807在64位系统上。将其与0…4,294,967,295的UInt32范围进行比较。U (UInt32)表示无符号。

考虑以下错误:

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

所以你只需要确保你的输入参数是在UInt32范围内,你不需要输出是在这个范围之外。

其他回答

Swift 4.2编辑

从Swift 4.2开始,你可以使用Swift自己的本地函数,而不是使用导入的C函数arc4random_uniform()。

// Generates integers starting with 0 up to, and including, 10
Int.random(in: 0 ... 10)

你也可以使用random(in:)来获取其他原始值的随机值;比如Int型,Double型,Float型,甚至Bool型。

Swift版本< 4.2

该方法将在给定的最小值和最大值之间生成一个随机Int值

func randomInt(min: Int, max: Int) -> Int {
    return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}

使用arc4random_uniform ()

用法:

arc4random_uniform(someNumber: UInt32) -> UInt32

这将为您提供0到someNumber - 1范围内的随机整数。

UInt32的最大值为4,294,967,295(即2^32 - 1)。

例子:

抛硬币 让flip = arc4random_uniform(2) // 0或1 骰子滚 Let roll = arc4random_uniform(6) + 1 // 1 十月的任意一天 Let day = arc4random_uniform(31) + 1 // 1 90年代的任意一年 Let year = 1990 + arc4random_uniform(10)

一般形式:

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

其中number、max、min为UInt32。

是什么……

arc4random ()

你也可以通过使用arc4random()得到一个随机数,它会产生一个在0到2^32-1之间的UInt32。因此,要得到一个介于0和x-1之间的随机数,可以将其除以x并取余数。或者换句话说,使用余数运算符(%):

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

然而,这会产生轻微的模数偏差(参见这里和这里),所以这就是为什么建议使用arc4random_uniform()。

从Int转换到Int

通常情况下,为了在Int和UInt32之间来回转换,这样做是很好的:

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

但问题是,Int的范围是-2,147,483,648…2,147,483,647在32位系统和范围-9,223,372,036,854,775,808…9223,372,036,854,775,807在64位系统上。将其与0…4,294,967,295的UInt32范围进行比较。U (UInt32)表示无符号。

考虑以下错误:

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

所以你只需要确保你的输入参数是在UInt32范围内,你不需要输出是在这个范围之外。

下面的代码将产生一个0到255之间的安全随机数:

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

你这样称呼它:

print(UInt8.random)

对于更大的数字,它会变得更复杂。 这是我能想到的最好的:

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

这些方法使用一个额外的随机数来确定有多少uint8将被用于创建随机数。最后一行将[UInt8]转换为UInt16或UInt32。

我不知道后两个是否还算真正的随机,但你可以根据自己的喜好进行调整:)

var randomNumber = Int(arc4random_uniform(UInt32(5)))

这里5将确保随机数是从0到4生成的。您可以设置相应的值。

@jstn的回答很好,但有点啰嗦。Swift被称为面向协议的语言,因此我们可以通过为协议扩展添加默认实现,而不必为整数族中的每个类实现样板代码来实现相同的结果。

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

现在我们可以做:

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

其他的整数类都是可以的。