首页 文章

如何在Apple的Swift语言中生成一个随机数?

提问于
浏览
380

我意识到Swift书提供了随机数生成器的实现 . 最好的做法是将此实现复制并粘贴到自己的程序中吗?或者是否有一个我们现在可以使用的库?

25 回答

  • 2

    我已经能够使用 rand() 来获得一个随机的CInt . 您可以使用以下内容将其设为Int:

    let myVar: Int = Int(rand())
    

    您可以使用自己喜欢的C随机函数,如果需要,只需将值转换为Int即可 .

  • 21

    我用过这段代码:

    var k: Int = random() % 10;
    
  • 16

    Edit for 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 versions < 4.2

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

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

    您可以像这样使用 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())
    
  • 5

    Swift 4.2

    Swift 4.2在标准库中包含了一个原生且功能齐全的随机数API . (Swift Evolution proposal SE-0202

    let intBetween0to9 = Int.random(in: 0...9) 
    let doubleBetween0to1 = Double.random(in: 0...1)
    

    所有数字类型都具有静态random(in:),它取范围并返回给定范围内的随机数

  • 24
    var randomNumber = Int(arc4random_uniform(UInt32(**5**)))
    

    这里5将确保随机数生成为零到五 . 您可以相应地设置值 .

  • 6

    使用标准库函数获得高质量的随机数: arc4random()arc4random_uniform() ,就像在Objective-C中一样 .

    它们位于 Darwin 模块中,因此如果您尚未导入 AppKitUIKitFoundation (为您导入它),则需要 import Darwin .

    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()
    
  • 2

    Swift 4.2,Xcode 10.1 .

    对于iOS,macOS和tvOS,您可以在Xcode的框架 GameKit 中使用 system-wide random source . 在这里你可以找到 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)
    
  • 113

    Edit: Updated for 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)
    
  • 0

    我使用此代码生成一个随机数:

    //
    //  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]
        }
    }
    
  • 58

    自Swift 4.2以来

    有一组新的API:

    let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
    let randomDouble = Double.random(in: 1 ... 10)
    
    • 所有 numeric 类型现在都有random(in:)方法,需要 range .

    • 它返回在该范围内均匀分布的数字 .


    TL;DR

    那么,“好”旧方式有什么问题?

    • 您必须使用导入的C API(它们在平台之间是不同的) .

    • 此外......

    如果我告诉你随机不是随机的怎么办?

    如果使用 arc4random() (计算余数),如 arc4random() % aNumber ,结果 is not 均匀分布在 0aNumber 之间 . 有一个叫 Modulo bias 的问题 .

    Modulo bias

    通常,该函数会在 0 和MAX之间生成一个随机数(取决于类型等) . 为了做一个快速简单的例子,假设最大数字是 7 并且您关心 0 ..< 2 范围内的随机数(或者如果您愿意,则关注区间[0,3]) .

    个人号码的 probabilities 是:

    • 0:3/8 = 37.5%

    • 1:3/8 = 37.5%

    • 2:2/8 = 25%

    换句话说,你最终会以0或1结束 . 当然,请记住,这是非常简化的,并且MAX数字要高得多,这使得它更多"fair" .

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

  • 488

    @jstn's answer很好,但有点冗长 . 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()
    

    和所有其他整数类都可以 .

  • 5

    10(0-9)之间随机数的例子;

    import UIKit
    
    let randomNumber = Int(arc4random_uniform(10))
    

    非常简单的代码 - 简单而简短 .

  • 0

    你可以像在C中那样做:

    let randomNumber = arc4random()
    

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

  • 4

    详情

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

    样本结果

    enter image description here

  • 9

    从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之类的内容来塑造随机分布 . 这需要更多的解释,但如果你感兴趣,你可以read my tutorial on GameplayKit random numbers .

  • 8

    以下代码将生成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] 转换为 UInt16UInt32 .

    我不知道最后两个仍然算是真正的随机,但你可以调整它你的喜好:)

  • 1

    Swift 4.2

    再见,导入Foundation C lib 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?如果让 .

    • 您使用random(in :)生成随机Double,Float或CGFloat和random()以返回随机Bool .

    More @ Official

  • 1

    这是一个很好地完成工作的图书馆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
        }
    }
    
  • 16
    let MAX : UInt32 = 9
     let MIN : UInt32 = 1
    
        func randomNumber()
    {
        var random_number = Int(arc4random_uniform(MAX) + MIN)
        print ("random = ", random_number);
    }
    
  • 32

    使用 arc4random_uniform(n) 表示0到n-1之间的随机整数 .

    let diceRoll = Int(arc4random_uniform(6) + 1)
    

    将结果转换为Int,这样您就不必将变量显式键入 UInt32 (这似乎是非Swifty) .

  • 18

    在某些版本的Xcode中没有arc4Random_uniform()(在7.1中它运行但不为我自动完成) . 你可以这样做 .

    从0-5生成随机数 . 第一

    import GameplayKit
    

    然后

    let diceRoll = GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
    
  • 11

    我想补充一下现有的答案,即Swift书中的随机数生成器示例是一个线性同余生成器(LCG),它是一个非常有限的,并且根本不应该是重要的 . 和 a LCG should never be used for cryptographic purposes .

    arc4random() 好多了,可以用于大多数用途,但是 should not be used for cryptographic purposes.

    如果您想要保证加密安全的内容,请使用 SecCopyRandomBytes() . 请注意,如果您将随机数生成器构建为某些内容,则其他人可能最终(误)将其用于加密目的(例如密码,密钥或盐生成),那么即使您需要,也应该考虑使用 SecCopyRandomBytes() 不太需要 .

  • 0

    使用arc4random_uniform()

    Usage:

    arc4random_uniform(someNumber: UInt32) -> UInt32

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

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

    Examples:

    • 硬币翻转
    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)
    

    General form:

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

    其中 numbermaxminUInt32 .

    怎么样......

    arc4random()

    您还可以使用 arc4random() 获取一个随机数,它产生一个介于0和2 ^ 32-1之间的 UInt32 . 因此,要获得 0x-1 之间的随机数,您可以将其除以 x 并取余数 . 或者换句话说,使用Remainder Operator (%)

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

    但是,这会产生轻微的modulo bias(另请参见herehere),因此建议使用 arc4random_uniform() .

    Converting to and from Int

    通常,做这样的事情以便在 Int 和_514637之间来回转换会很好:

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

    但问题是Int在32位系统上的范围为 -2,147,483,648...2,147,483,647 ,在64位系统上的范围为 -9,223,372,036,854,775,808...9,223,372,036,854,775,807 . 将其与 0...4,294,967,295UInt32 范围进行比较 . UInt32U 表示未签名 .

    请考虑以下错误:

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

    因此,您只需确保输入参数在 UInt32 范围内,并且您不需要输出超出该范围 .

  • 329

    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)
    

相关问题