Swift生成0到1之间的随机浮点数

88

在Swift中,我试图获取一个0到1之间的随机浮点数,但是似乎无法进行类型转换。

func randomCGFloat() -> CGFloat {
    return CGFloat(arc4random()) / UINT32_MAX
}

我遇到了一个 'CGFloat' 无法转换为 'UInt8' 的错误。

正在运行 Xcode 6。


11个回答

126

这是针对 Int、Double、Float、CGFloat随机数 扩展。

Swift 3、4、5 语法。

import Foundation
import CoreGraphics

// MARK: Int Extension

public extension Int {

    /// Returns a random Int point number between 0 and Int.max.
    static var random: Int {
        return Int.random(n: Int.max)
    }

    /// Random integer between 0 and n-1.
    ///
    /// - Parameter n:  Interval max
    /// - Returns:      Returns a random Int point number between 0 and n max
    static func random(n: Int) -> Int {
        return Int(arc4random_uniform(UInt32(n)))
    }

    ///  Random integer between min and max
    ///
    /// - Parameters:
    ///   - min:    Interval minimun
    ///   - max:    Interval max
    /// - Returns:  Returns a random Int point number between 0 and n max
    static func random(min: Int, max: Int) -> Int {
        return Int.random(n: max - min + 1) + min

    }
}

// MARK: Double Extension

public extension Double {

    /// Returns a random floating point number between 0.0 and 1.0, inclusive.
    static var random: Double {
        return Double(arc4random()) / 0xFFFFFFFF
    }

    /// Random double between 0 and n-1.
    ///
    /// - Parameter n:  Interval max
    /// - Returns:      Returns a random double point number between 0 and n max
    static func random(min: Double, max: Double) -> Double {
        return Double.random * (max - min) + min
    }
}

// MARK: Float Extension

public extension Float {

    /// Returns a random floating point number between 0.0 and 1.0, inclusive.
    static var random: Float {
        return Float(arc4random()) / 0xFFFFFFFF
    }

    /// Random float between 0 and n-1.
    ///
    /// - Parameter n:  Interval max
    /// - Returns:      Returns a random float point number between 0 and n max
    static func random(min: Float, max: Float) -> Float {
        return Float.random * (max - min) + min
    }
}

// MARK: CGFloat Extension

public extension CGFloat {

    /// Randomly returns either 1.0 or -1.0.
    static var randomSign: CGFloat {
        return (arc4random_uniform(2) == 0) ? 1.0 : -1.0
    }

    /// Returns a random floating point number between 0.0 and 1.0, inclusive.
    static var random: CGFloat {
        return CGFloat(Float.random)
    }

    /// Random CGFloat between 0 and n-1.
    ///
    /// - Parameter n:  Interval max
    /// - Returns:      Returns a random CGFloat point number between 0 and n max
    static func random(min: CGFloat, max: CGFloat) -> CGFloat {
        return CGFloat.random * (max - min) + min
    }
}

用途:

let randomNumDouble  = Double.random(min: 0.00, max: 23.50)
let randomNumInt     = Int.random(min: 56, max: 992)
let randomNumFloat   = Float.random(min: 6.98, max: 923.09)
let randomNumCGFloat = CGFloat.random(min: 6.98, max: 923.09)

1
@DaRk-_-D0G,我在努力理解这个数学问题。为什么在使用Float和Double的random()函数时要除以0xFFFFFFFF? - Crystal
2
@EthanMick 添加更新以适应Swift 2的外观 ;) - YannSteph
1
尝试使用 CGFloat.random(min:0.1, max:10.1) 时出现“无法调用非函数类型'CGFloat'”错误。 - Andrew_STOP_RU_WAR_IN_UA
1
Swift 4 不需要更新。 - RyuX51
2
@YannickSteph 在 Swift 5 和 Xcode 10.2 中,行 return Float(arc4random()) / 0xFFFFFFFF 现在会出现警告:'4294967295' is not exactly representable as 'Float'; it becomes '4294967296'。有什么解决这个警告的想法吗?我想改为除以 Float(UInt32.max) - peacetype
显示剩余7条评论

105
尝试将除数也初始化为浮点数,如下所示:

尝试将除数也初始化为浮点数,如下所示:

CGFloat(Float(arc4random()) / Float(UINT32_MAX))

谢谢,我曾尝试使用CGFloat转换分母,但没有成功。但是现在看来,先将其转换为float,然后再转换为CGFloat可以实现。 - Joe_Schmoe
2
不需要通过 Float - 只需使用 CGFloat(arc4random()) / CGFloat(UInt32.max) - Hamish
@Joe_Schmoe 如果你不使用 Float/CGFloat,那么这个除法就是一个小整数除以一个大整数,结果总是返回 0。 - Teng L
4
在 Swift 4.2 中,只需要使用以下代码:Float.random(in: 0..<1)。 - Andrew Paul Simmons

52

Swift 4.2:

let randomFloat = Float.random(in: 0..<1)

26

更新 Sandy Chapman 的回答,以适应 Swift 3:

extension ClosedRange where Bound : FloatingPoint {
    public func random() -> Bound {
        let range = self.upperBound - self.lowerBound
        let randomValue = (Bound(arc4random_uniform(UINT32_MAX)) / Bound(UINT32_MAX)) * range + self.lowerBound
        return randomValue
    }
}

现在你可以像这样说:(-1.0...1.0).random()

编辑 我认为今天(Swift 4)我会这样写:

extension ClosedRange where Bound : FloatingPoint {
    public func random() -> Bound {
        let max = UInt32.max
        return
            Bound(arc4random_uniform(max)) /
            Bound(max) *
            (upperBound - lowerBound) +
            lowerBound
    }
}

注意,Swift 4.2引入了本地随机数生成,因此所有这些都变得不重要了。


16

这个框架在Swift中生成随机数数据方面做得很好:https://github.com/thellimist/SwiftRandom/blob/master/SwiftRandom/Randoms.swift

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

当编译为“通用iOS设备”时,这无法工作。 0xffffffff无法适应Int中。请尝试使用CGFloat(UInt32.max)代替。 - neoneye

13

以下是对IntervalType类型的扩展,用于实现此功能:

extension IntervalType {
    public func random() -> Bound {
        let range = (self.end as! Double) - (self.start as! Double)
        let randomValue = (Double(arc4random_uniform(UINT32_MAX)) / Double(UINT32_MAX)) * range + (self.start as! Double)
        return randomValue as! Bound
    }
}

使用此扩展,您可以使用间隔语法生成一个区间,然后在该区间内获取一个随机值:

(0.0...1.0).random()

加法

如果您想对 Int 进行相同的操作,那么可以使用以下扩展来实现 CollectionType 协议:

extension CollectionType {
    public func random() -> Self._Element {
        if let startIndex = self.startIndex as? Int {
            let start = UInt32(startIndex)
            let end = UInt32(self.endIndex as! Int)
            return self[Int(arc4random_uniform(end - start) + start) as! Self.Index]
        }
        var generator = self.generate()
        var count = arc4random_uniform(UInt32(self.count as! Int))
        while count > 0 {
            generator.next()
            count = count - 1
        }
        return generator.next() as! Self._Element
    }
}

Int 不使用 IntervalType,而是使用 Range。在 CollectionType 类型上这么做的好处是它会自动应用到 DictionaryArray 类型。

示例:

(0...10).random()               // Ex: 6
["A", "B", "C"].random()        // Ex: "B"
["X":1, "Y":2, "Z":3].random()  // Ex: (.0: "Y", .1: 2)

如果我们要扩展IntervalType,让它也适用于Int类型,该怎么做呢?虽然这不是原始问题,但似乎是下一步的合理选择。 - Joe_Schmoe
1
@Joe_Schmoe:我已经更新了我的答案,详细介绍了在IntDictionaryArray上使用.random()的细节。 - Sandy Chapman
我非常喜欢这个解决方案,但我正在更新以包括更好的通用实践。 - tbondwilkinson
1
在Swift 3中出现了问题。 - matt

12

Swift 5

let randomFloat = CGFloat.random(in: 0...1)

6

看起来jmduke建议的方法在Playground中是有效的,只需要对函数进行一些小修改:

func randomCGFloat() -> Float {
    return Float(arc4random()) /  Float(UInt32.max)
}

从Swift文档和drewag的注释可以得知,类型转换必须是显式的。文档中给出的示例如下:

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to be of type Double

3
drand48()

如果需要[双精度],它的范围是0到1。就是这样。


优雅的解决方案,别忘了 srand48(Int(Date().timeIntervalSince1970)) - AlKozin

1

基于YannickSteph的答案

为了使其适用于任何浮点类型,例如DoubleFloatCGFloat等,您可以为BinaryFloatingPoint类型创建一个扩展:

extension BinaryFloatingPoint {

    /// Returns a random floating point number between 0.0 and 1.0, inclusive.
    public static var random: Self {
        return Self(arc4random()) / 0xFFFFFFFF
    }

    /// Random double between 0 and n-1.
    ///
    /// - Parameter n:  Interval max
    /// - Returns:      Returns a random double point number between 0 and n max
    public static func random(min: Self, max: Self) -> Self {
        return Self.random * (max - min) + min
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接