我意识到Swift书提供了随机数生成器的实现.最好的做法是将此实现复制并粘贴到自己的程序中吗?或者是否有一个我们现在可以使用的库?
从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随机数教程.
你可以像在C中那样做:
let randomNumber = arc4random()
randomNumber
被推断为类型UInt32
(32位无符号整数)
使用标准库函数来获得高质量的随机数:random()
或者random()
,就像在Objective-C中一样.
他们是在random()
模块,因此,如果您还没有进口random()
,random()
或random()
(其中进口为你),你将需要random()
.
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()
这是一个很好地完成工作的库 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 } }
使用arc4random_uniform(n)
为0和n-1之间的随机整数.
let diceRoll = Int(arc4random_uniform(6) + 1)
将结果转换为Int,这样您就不必明确键入您的变量UInt32
(这似乎是非Swifty).
10(0-9)之间随机数的例子;
import UIKit let randomNumber = Int(arc4random_uniform(10))
非常简单的代码 - 简单而简短.
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
20世纪90年代的随机年份
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
范围内,并且您不需要超出该范围的输出.
我用过这段代码:
var k: Int = random() % 10;
我已经能够用来rand()
获得一个随机的CInt.您可以使用以下内容将其设为Int:
let myVar: Int = Int(rand())
您可以使用自己喜欢的C随机函数,如果需要,只需将值转换为Int即可.
有一组新的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:3/8 = 37.5%
1:3/8 = 37.5%
2:2/8 = 25%
换句话说,您更有可能以0或1而不是2结尾。当然,请记住,这是极其简化的,并且MAX数更高,使其更加“公平”。
此问题是由解决随机统一- SE-0202的雨燕4.2
let MAX : UInt32 = 9 let MIN : UInt32 = 1 func randomNumber() { var random_number = Int(arc4random_uniform(MAX) + MIN) print ("random = ", random_number); }
var randomNumber = Int(arc4random_uniform(UInt32(**5**)))
这里5将确保生成随机数,但是从零到五.您可以相应地设置值.
编辑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))) }
在某些版本的Xcode中没有arc4Random_uniform()(在7.1中它运行但不为我自动完成).你可以这样做.
从0-5生成随机数.第一
import GameplayKit
然后
let diceRoll = GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
在Swift 4.2中,您可以通过调用所需的random()
任何数字类型的方法来生成随机数,从而提供您想要使用的范围.例如,这会生成1到9范围内的随机数,包括两侧
let randInt = Int.random(in: 1..<10)
还有其他类型
let randFloat = Float.random(in: 1..<20) let randDouble = Double.random(in: 1...30) let randCGFloat = CGFloat.random(in: 1...40)
我想补充一下现有的答案,即Swift本书中的随机数生成器示例是一个线性同余生成器(LCG),它是一个非常有限的一个,不应该是除了必须琐碎的例子,其中随机性的质量没有什么都不重要.并且LCG绝不应该用于加密目的.
arc4random()
更好,可用于大多数目的,但不应再用于加密目的.
如果您想要保证加密安全的内容,请使用SecCopyRandomBytes()
.请注意,如果您将随机数生成器构建为某些内容,则其他人可能最终(误)将其用于加密目的(例如密码,密钥或盐生成),那么您应该考虑使用SecCopyRandomBytes()
,即使您的需要不是非常需要.
编辑:已 更新为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)
@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()
和所有其他整数类都可以.