我意识到Swift书籍提供了一个随机数生成器的实现。复制和粘贴这个实现是最佳实践吗?或者有没有这样的库,我们现在就可以使用?
我已经能够使用rand()来获得一个随机的CInt。你可以使用这样的方法使它成为Int型:
let myVar: Int = Int(rand())
你可以使用你最喜欢的C随机函数,并在需要时将值转换为Int。
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()
对于0到n-1之间的随机整数,使用arc4random_uniform(n)。
let diceRoll = Int(arc4random_uniform(6) + 1)
将结果转换为Int,这样你就不必显式地将你的vars类型为UInt32(这看起来不像swifty)。
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)))
}
编辑:为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 randomNumber = Int(arc4random_uniform(UInt32(5)))
这里5将确保随机数是从0到4生成的。您可以设置相应的值。
你可以像这样使用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())
我想补充现有的答案,在Swift书中的随机数生成器的例子是一个线性同余生成器(LCG),这是一个非常有限的一个,不应该除了必须平凡的例子,其中随机性的质量根本不重要。LCG永远不应该用于加密目的。
Arc4random()要好得多,可以用于大多数目的,但不应该用于加密目的。
如果您想要保证加密安全的内容,请使用SecCopyRandomBytes()。请注意,如果您将随机数生成器构建到某个东西中,其他人可能最终(错误地)将其用于加密目的(例如密码、密钥或盐生成),那么无论如何您都应该考虑使用SecCopyRandomBytes(),即使您的需要并不完全需要这样做。
从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随机数的教程。
使用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范围内,你不需要输出是在这个范围之外。
这里有一个库可以很好地完成这项工作 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
}
}
在某些版本的Xcode中没有arc4Random_uniform()(在7.1中运行,但对我来说不自动完成)。你可以这样做。
从0-5中生成一个随机数。 第一个
import GameplayKit
Then
let diceRoll = GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
例如10(0-9)之间的随机数;
import UIKit
let randomNumber = Int(arc4random_uniform(10))
非常简单的代码-简单和简短。
let MAX : UInt32 = 9
let MIN : UInt32 = 1
func randomNumber()
{
var random_number = Int(arc4random_uniform(MAX) + MIN)
print ("random = ", random_number);
}
我用这段代码生成一个随机数:
//
// 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]
}
}
下面的代码将产生一个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。
我不知道后两个是否还算真正的随机,但你可以根据自己的喜好进行调整:)
@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()
其他的整数类都是可以的。
细节
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]
}
样品结果
在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 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 -随机统一解决了这个问题
斯威夫特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:),它接受范围并返回给定范围内的随机数
更新日期: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)
斯威夫特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()
使用random(in:)从范围中生成随机数字。 randomElement()返回nil如果范围是空的,所以你打开返回的Int?用if let。 使用random(in:)生成随机Double、Float或CGFloat,使用random()返回随机Bool类型。
更多@官方
Xcode 14, swift 5
public extension Array where Element == Int {
static func generateNonRepeatedRandom(size: Int) -> [Int] {
guard size > 0 else {
return [Int]()
}
return Array(0..<size).shuffled()
}
}
使用方法:
let array = Array.generateNonRepeatedRandom(size: 15)
print(array)
输出
推荐文章
- 如何使用@Binding变量实现自定义初始化
- Swift设置为Array
- 如何设置回退按钮文本在Swift
- 我如何能在Swift扩展类型化数组?
- Swift类错误:属性未在super处初始化。init调用
- Swift to Objective-C头未在Xcode 6中创建
- 在Swift中将字典转换为JSON
- 我如何模仿地图应用程序的底部表格?
- 改变导航栏后退按钮的颜色
- 如何在c++中生成一个随机数?
- Swift是否支持文档生成?
- 如何复制文本到剪贴板/剪贴板与Swift
- 如何在Swift中返回数组的前5个对象?
- 在Swift中以编程方式返回到以前的ViewController
- 按下UINavigationController后栏按钮时执行动作