我意识到Swift书提供了一个随机数生成器的实现。将这种实现复制并粘贴到自己的程序中的最佳实践是吗?还是有一个可以立即使用的库?
我意识到Swift书提供了一个随机数生成器的实现。将这种实现复制并粘贴到自己的程序中的最佳实践是吗?还是有一个可以立即使用的库?
Answers:
迅捷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()
使用arc4random_uniform(n)
为0和n-1之间的随机整数。
let diceRoll = Int(arc4random_uniform(6) + 1)
将结果强制转换为Int,这样您就不必显式地输入vars为UInt32
(这似乎是不迅速的)。
0
。在您的代码中,diceRoll
可能是0
。只是说...
Int(arc4random_uniform(6)+1)
。
arc3random_uniform(n)
为a UInt32(n)
。
编辑: 更新为Swift 3.0
arc4random
在Swift中可以很好地工作,但是基本函数仅限于32位整数类型(Int
在iPhone 5S和现代Mac上为64位)。这是一个用整数文字表示的随机数类型的通用函数:
public func arc4random<T: ExpressibleByIntegerLiteral>(_ type: T.Type) -> T {
var r: T = 0
arc4random_buf(&r, MemoryLayout<T>.size)
return r
}
我们可以使用这个新的泛型函数来扩展UInt64
,添加边界参数并减轻模偏差。(这是从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
}
}
这样,我们可以扩展Int64
为相同的参数,以处理溢出:
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
}
}
}
为了完成家庭...
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
}
}
}
毕竟,我们最终可以做这样的事情:
let diceRoll = UInt64.random(lower: 1, upper: 7)
var r = arc4random(UInt64)
。请指教您在这里是什么意思?
arc4random
(在第一个代码块中定义)的参数UInt64
为a Type
。
arc4random_buf
。这些扩展的目的是确切地arc4random_uniform
执行除64位类型以外的操作(缓解模偏差)。
为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)))
}
我使用以下代码:
var k: Int = random() % 10;
random is unavailable in Swift: Use arc4random instead.
从iOS 9开始,您可以使用新的GameplayKit类以多种方式生成随机数。
您有四种源类型可供选择:常规随机源(未命名,由系统决定其作用),线性同余,ARC4和Mersenne Twister。这些会生成随机的整数,浮点数和布尔值。
在最简单的级别上,您可以从系统内置的随机源生成一个随机数,如下所示:
GKRandomSource.sharedRandom().nextInt()
生成的数字介于-2,147,483,648和2,147,483,647之间。如果您希望数字介于0到上限(不包括上限)之间,请使用以下代码:
GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
GameplayKit内置了一些方便的构造函数,可以与骰子一起使用。例如,您可以像这样滚动六个面的模具:
let d6 = GKRandomDistribution.d6()
d6.nextInt()
另外,您可以使用GKShuffledDistribution之类的东西来塑造随机分布。这需要更多解释,但是如果您有兴趣,可以阅读我有关GameplayKit随机数的教程。
import GameplayKit
。Swift 3将语法更改为GKRandomSource.sharedRandom().nextInt(upperBound: 6)
您可以像在C中一样进行操作:
let randomNumber = arc4random()
randomNumber
推断为类型UInt32
(32位无符号整数)
rand
,arc4random
,drand48
和朋友都是在Darwin
模块。如果您要构建Cocoa,UIKit或Foundation应用程序,则已经为您导入了该应用程序,但是您需要import Darwin
在操场上。
arc4random_uniform()
用法:
arc4random_uniform(someNumber: UInt32) -> UInt32
这使您可以在范围内的随机整数0
来someNumber - 1
。
的最大值UInt32
是4,294,967,295(即2^32 - 1
)。
例子:
硬币翻转
let flip = arc4random_uniform(2) // 0 or 1
骰子卷
let roll = arc4random_uniform(6) + 1 // 1...6
十月的随机日
let day = arc4random_uniform(31) + 1 // 1...31
1990年代为随机年份
let year = 1990 + arc4random_uniform(10)
一般形式:
let number = min + arc4random_uniform(max - min + 1)
其中number
,max
和min
是UInt32
。
arc4random()
您还可以使用来获得一个随机数arc4random()
,它会产生UInt32
0到2 ^ 32-1之间的值。因此,要在0
和之间获得一个随机数x-1
,可以将其除以x
余数。换句话说,请使用余数运算符(%):
let number = arc4random() % 5 // 0...4
但是,这会产生轻微的模偏置(另请参见此处和此处),因此arc4random_uniform()
建议这样做。
往返 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...9,223,372,036,854,775,807
64个系统。将此与的UInt32
范围进行比较0...4,294,967,295
。该U
的UInt32
手段无符号。
请考虑以下错误:
UInt32(-1) // negative numbers cause integer overflow error
UInt32(4294967296) // numbers greater than 4,294,967,295 cause integer overflow error
因此,您只需要确保输入参数在此UInt32
范围内,并且也不需要超出该范围的输出即可。
我已经能够用来rand()
获取随机CInt。您可以使用以下方法将其设置为Int:
let myVar: Int = Int(rand())
您可以使用自己喜欢的C随机函数,并根据需要将其转换为Int值。
random()
,它返回一个Int
而不是UInt32
-就像提到的@SomeGuy一样,srandom(arc4random())
在使用它之前只需在任意位置调用一次,以确保它对于程序的每次执行都具有不同的随机种子。
@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()
其他所有整数类都可以。
这是一个很好地完成工作的库 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
}
}
我想补充一下现有的答案,Swift书中的随机数生成器示例是线性同余生成器(LCG),它是一个受严格限制的示例,除了必须具备的琐碎示例(随机性不没关系。而且一个LCG不应该被用于密码的目的。
arc4random()
更好,可以用于大多数目的,但同样不应用于加密目的。
如果您想要某种可以保证密码安全的东西,请使用SecCopyRandomBytes()
。请注意,如果您将某个随机数生成器构建到某个东西中,那么其他人可能最终会(误用)该数字生成器用于加密目的(例如密码,密钥或盐生成),那么无论您SecCopyRandomBytes()
是否需要,都应考虑使用它。完全不需要。
有一组新的API:
let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomDouble = Double.random(in: 1 ... 10)
现在,所有数字类型都有采用的random(in:)
方法range
。
它返回在该范围内均匀分布的数字。
TL; DR
您必须使用导入的C API (它们在平台之间是不同的)。
而且...
如果我告诉您随机不是那样怎么办?
如果使用arc4random()
(计算余数)一样arc4random() % aNumber
,则结果不会在0
和之间均匀分布aNumber
。有一个问题叫做模偏差。
模偏差
通常情况下,该函数生成之间的随机数0
和MAX (取决于类型等)。为了简单快速地举一个例子,假设最大数量为,7
并且您关心范围内的随机数0 ..< 2
(如果愿意,则为区间[0,3])。
单个数字的概率为:
换句话说,您更有可能以0或1而不是2结尾。当然,请记住,这是极其简化的,并且MAX数更高,使其更加“公平”。
此问题是由解决随机统一- SE-0202的雨燕4.2
在某些Xcode版本中没有arc4Random_uniform()(在7.1中可以运行,但对我而言不会自动完成)。您可以改为这样做。
生成0-5之间的随机数。第一
import GameplayKit
然后
let diceRoll = GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
var randomNumber = Int(arc4random_uniform(UInt32(5)))
这里5将确保随机数是从零到四生成的。您可以相应地设置值。
斯威夫特4.2
再见,导入Foundation C库 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()
以下代码将产生一个介于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
将使用多少s来创建随机数。最后一行将转换[UInt8]
为UInt16
或UInt32
。
我不知道后两个是否仍算是真正随机的,但是您可以根据自己的喜好调整它:)
斯威夫特4.2
Swift 4.2在标准库中包含了一个本机且功能相当齐全的随机数API。(Swift Evolution提案SE-0202)
let intBetween0to9 = Int.random(in: 0...9)
let doubleBetween0to1 = Double.random(in: 0...1)
所有数字类型均具有静态随机数(in :),该随机数将获取范围并返回给定范围内的随机数
雨燕4.2,Xcode的10.1。
对于iOS,macOS和tvOS,您可以在Xcode的框架中使用系统范围的随机源GameKit
。在这里,您可以找到GKRandomSource
带有其sharedRandom()
类方法的类:
import GameKit
let number: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
func randomGenerator() -> Int {
let random = GKRandomSource.sharedRandom().nextInt(upperBound: number.count)
return number[random]
}
randomGenerator()
或仅使用randomElement()
返回集合的随机元素的方法:
let number: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let randomNumber = number.randomElement()!
print(randomNumber)
您可以这样使用GeneratorOf
:
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())
我使用以下代码生成一个随机数:
//
// 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]
}
}
xCode 9.1,Swift 4
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
}
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]
不要忘记在此处添加面向数学的解决方案(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]
}
}
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]
}