Swift ngẫu nhiên float giữa 0 và 1


82

Trong Swift, tôi đang cố gắng lấy một số thực ngẫu nhiên từ 0 đến 1 nhưng dường như tôi không thể làm cho các chuyển đổi kiểu hoạt động.

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

Tôi nhận được lỗi 'CGFloat' không thể chuyển đổi thành lỗi 'UInt8'

Chạy Xcode 6.


Câu trả lời:


103

Hãy thử khởi tạo ước số dưới dạng float, a la:

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

Cảm ơn, tôi đã thử đúc mẫu số bằng CGFloat và điều đó không hoạt động. Nhưng nó trông như thể truyền đến một phao trước, sau đó đến một CGFloat hoạt động.
Joe_Schmoe,

2
Không cần phải đi qua Float- chỉ cần CGFloat(arc4random()) / CGFloat(UInt32.max).
Hamish

@Joe_Schmoe nếu bạn không đi qua phao / CGFloat bộ phận là một số nguyên nhỏ chia cho một số nguyên lớn, và sẽ luôn luôn trở về 0.
Teng L

4
nhưng trong nhanh chóng 4.2 chỉ cần sử dụng: Float.random (trong: 0 .. <1)
Andrew Paul Simmons

116

Đây là phần mở rộng cho số ngẫu nhiên của Int, Double, Float, CGFloat

Cú pháp 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
    }
}

Sử dụng :

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 Tôi đang cố gắng hiểu toán học. Tại sao bạn chia cho 0xFFFFFFFF cho random () cho Float và Double?
Crystal

2
@EthanMick thêm Cập nhật cho Swift 2 nhìn;)
YannSteph

1
Lỗi "không thể gọi giá trị của loại không phải hàm 'CGFloat'" xuất hiện khi cố gắng sử dụng CGFloat.random (min: 0.1, max: 10.1)
Andrew

1
Không cần cập nhật cho Swift 4.
RyuX51 21/09/17

1
@YannickSteph Với Swift 5 và Xcode 10.2, dòng return Float(arc4random()) / 0xFFFFFFFFhiện nay đưa ra một cảnh báo: '4294967295' is not exactly representable as 'Float'; it becomes '4294967296'. Bất kỳ ý tưởng làm thế nào để giải quyết cảnh báo đó? Tôi đang nghĩ để chia cho Float(UInt32.max)thay vào đó.
Peacetype


26

Cập nhật câu trả lời của Sandy Chapman cho 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
    }
}

Bây giờ bạn có thể nói những thứ như thế nào (-1.0...1.0).random().

CHỈNH SỬA Tôi nghĩ hôm nay (Swift 4) Tôi sẽ viết một cái gì đó như thế này:

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

LƯU Ý Swift 4.2 giới thiệu việc tạo số ngẫu nhiên gốc và tất cả điều này trở thành tranh luận.


16

Đây là khung làm việc tốt trong việc tạo dữ liệu số ngẫu nhiên trong 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
    }
}

Điều này không hoạt động khi biên dịch cho Generic iOS device. 0xffffffff không phù hợp với Int. Hãy thử điều này thay vì CGFloat (UInt32.max)
neoneye

13

Dưới đây là phần mở rộng về IntervalTypeloại để thực hiện việc này:

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

Với tiện ích mở rộng này, bạn có thể sử dụng cú pháp khoảng thời gian để tạo khoảng thời gian và sau đó nhận giá trị ngẫu nhiên trong khoảng thời gian đó:

(0.0...1.0).random()

Thêm vào

Nếu bạn đang tìm cách làm tương tự cho Ints, thì bạn có thể sử dụng tiện ích mở rộng sau trên CollectionTypegiao thức:

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

Ints không sử dụng IntervalType. Họ sử dụng Rangethay thế. Lợi ích của việc này trên CollectionTypeloại là nó tự động được chuyển sang loại DictionaryArray.

Ví dụ:

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

làm thế nào để chúng ta có được điều này để làm việc cho Ints là tốt, không phải là câu hỏi ban đầu, nhưng có vẻ như một bước tiếp theo xảy ra nếu chúng ta mở rộng IntervalType
Joe_Schmoe

1
@Joe_Schmoe: Tôi đã cập nhật câu trả lời của tôi với các chi tiết về việc sử dụng .random()trên Ints, Dictionarys và Arrays
Sandy Chapman

Tôi thực sự thích giải pháp này, nhưng tôi cập nhật để bao gồm thông lệ chung tốt hơn
tbondwilkinson

1
Bị hỏng trong Swift 3.
matt


6

Những gì jmduke đề xuất dường như hoạt động trong Playground với một thay đổi nhỏ trong chức năng:

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

và lý do tại sao từ tài liệu nhanh chóng và như đã lưu ý bởi drawag: chuyển đổi loại phải rõ ràng, ví dụ trong tài liệu là:

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

2
drand48()

Trong trường hợp bạn cần [Double]. Giữa 0 và 1. Chỉ vậy thôi.


1

Dựa trên câu trả lời của YannickSteph

Để làm cho nó làm việc cho bất kỳ loại dấu chấm động, như Double, Float, CGFloat, vv, bạn có thể làm cho một phần mở rộng cho các BinaryFloatingPointloại:

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

1

Chi tiết

Xcode: 9.2, Swift 4

Giải pháp

extension BinaryInteger {

    static func rand(_ min: Self, _ max: Self) -> Self {
        let _min = min
        let difference = max+1 - _min
        return Self(arc4random_uniform(UInt32(difference))) + _min
    }
}

extension BinaryFloatingPoint {

    private func toInt() -> Int {
        // https://stackoverflow.com/q/49325962/4488252
        if let value = self as? CGFloat {
            return Int(value)
        }
        return Int(self)
    }

    static func rand(_ min: Self, _ max: Self, precision: Int) -> Self {

        if precision == 0 {
            let min = min.rounded(.down).toInt()
            let max = max.rounded(.down).toInt()
            return Self(Int.rand(min, max))
        }

        let delta = max - min
        let maxFloatPart = Self(pow(10.0, Double(precision)))
        let maxIntegerPart = (delta * maxFloatPart).rounded(.down).toInt()
        let randomValue = Int.rand(0, maxIntegerPart)
        let result = min + Self(randomValue)/maxFloatPart
        return Self((result*maxFloatPart).toInt())/maxFloatPart
    }
}

Sử dụng

print("\(Int.rand(1, 20))")
print("\(Float.rand(5.231233, 44.5, precision: 3))")
print("\(Double.rand(5.231233, 44.5, precision: 4))")
print("\(CGFloat.rand(5.231233, 44.5, precision: 6))")

Đầy đủ mẫu

import Foundation
import CoreGraphics

func run() {
    let min = 2.38945
    let max = 2.39865
    for _ in 0...100 {
        let precision = Int.rand(0, 5)
        print("Precision: \(precision)")
        floatSample(min: Float(min), max: Float(max), precision: precision)
        floatSample(min: Double(min), max: Double(max), precision: precision)
        floatSample(min: CGFloat(min), max: CGFloat(max), precision: precision)
        intSample(min: Int(1), max: Int(10000))
        print("")
    }
}

private func printResult<T: Comparable>(min: T, max: T, random: T) {
    let result = "\(T.self) rand[\(min), \(max)] = \(random)"
    print(result)
}

func floatSample<T: BinaryFloatingPoint>(min: T, max: T, precision: Int) {
    printResult(min: min, max: max, random: T.rand(min, max, precision: precision))
}

func intSample<T: BinaryInteger>(min: T, max: T) {
    printResult(min: min, max: max, random: T.rand(min, max))
}

Các kết quả

nhập mô tả hình ảnh ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.