Schneller Zufallsschwimmer zwischen 0 und 1


82

In Swift versuche ich, einen zufälligen Gleitkommawert zwischen 0 und 1 zu erhalten, aber ich kann die Typkonvertierungen anscheinend nicht zum Laufen bringen.

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

Ich erhalte ein "CGFloat", das nicht in einen "UInt8" -Fehler konvertierbar ist

Ausführen von Xcode 6.


Antworten:


103

Versuchen Sie, den Divisor auch als Float zu initialisieren, a la:

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

Danke, ich hatte versucht, den Nenner mit CGFloat zu besetzen, und das funktionierte nicht. Aber es sieht so aus, als würde das Casting zuerst auf einen Float und dann auf einen CGFloat funktionieren.
Joe_Schmoe

2
Keine Notwendigkeit zu gehen Float- nur CGFloat(arc4random()) / CGFloat(UInt32.max).
Hamish

@ Joe_Schmoe Wenn Sie nicht durch Float / CGFloat gehen, ist die Division eine kleine Ganzzahl geteilt durch eine große Ganzzahl und wird immer 0 zurückgeben.
Teng L

4
aber in Swift 4.2 verwenden Sie einfach: Float.random (in: 0 .. <1)
Andrew Paul Simmons

116

Dies ist eine Erweiterung für Zufallszahlen von Int, Double, Float, CGFloat

Schnelle 3 & 4 & 5-Syntax

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

Verwenden :

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 Ich versuche die Mathematik zu verstehen. Warum dividieren Sie für Float und Double durch 0xFFFFFFFF für random ()?
Kristall

2
@ EthanMick Update für Swift 2 Look hinzufügen;)
YannSteph

1
Beim Versuch, CGFloat.random (min: 0.1, max: 10.1) zu verwenden, wird der Fehler "Wert des Nichtfunktionstyps 'CGFloat' kann nicht aufgerufen" angezeigt
Andrew

1
Für Swift 4 ist keine Aktualisierung erforderlich.
RyuX51

1
@YannickSteph Mit Swift 5 und Xcode 10.2 gibt die Zeile return Float(arc4random()) / 0xFFFFFFFFjetzt eine Warnung aus : '4294967295' is not exactly representable as 'Float'; it becomes '4294967296'. Irgendeine Idee, wie man diese Warnung löst? Ich denke Float(UInt32.max)stattdessen zu teilen .
Friedenstyp


26

Aktualisierung der Antwort von Sandy Chapman für 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
    }
}

Jetzt können Sie Dinge wie sagen (-1.0...1.0).random().

EDIT Ich denke heute (Swift 4) würde ich so etwas schreiben:

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

ANMERKUNG Swift 4.2 führt die native Zufallszahlengenerierung ein und all dies wird strittig.


16

Hier leistet Framework gute Arbeit beim Generieren von Zufallszahlendaten in 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
    }
}

Dies funktioniert beim Kompilieren für nicht Generic iOS device. Das 0xffffffff passt nicht in ein Int. Versuchen Sie dies stattdessen CGFloat (UInt32.max)
neoneye

13

Unten finden Sie eine Erweiterung des IntervalTypeTyps dafür:

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

Mit dieser Erweiterung können Sie die Intervallsyntax verwenden, um ein Intervall zu generieren und dann einen zufälligen Wert in diesem Intervall abzurufen:

(0.0...1.0).random()

Zusatz

Wenn Sie dasselbe für Ints tun möchten, können Sie die folgende Erweiterung des CollectionTypeProtokolls verwenden:

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 benutze das nicht IntervalType. Sie verwenden Rangestattdessen. Dies hat den Vorteil, CollectionTypedass der Typ automatisch auf die Typen Dictionaryund übertragen wird Array.

Beispiele:

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

Wie bringen wir das auch für Ints zum Laufen, nicht die ursprüngliche Frage, aber es scheint ein wahrscheinlicher nächster Schritt zu sein, wenn wir IntervalType
Joe_Schmoe

1
@ Joe_Schmoe: Ich habe meine Antwort mit den Details zur Verwendung .random()von Ints, Dictionarys und Arrays aktualisiert
Sandy Chapman

Ich mag diese Lösung wirklich, aber ich aktualisiere, um bessere allgemeine Praktiken aufzunehmen
tbondwilkinson

1
Gebrochen in Swift 3.
Matt


6

Was jmduke vorgeschlagen hat, scheint auf dem Spielplatz mit einer kleinen Änderung der Funktion zu funktionieren:

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

und der Grund, warum aus dem schnellen Dokument und wie von drawag angegeben: Typkonvertierung explizit sein muss, ist das Beispiel im Dokument:

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

Falls Sie [Double] benötigen. Zwischen 0 und 1. Das ist alles.


1

Basierend auf der Antwort von YannickSteph

Um es Arbeit für jeden Gleitkommatyps, wie Double, Float, CGFloatusw., können Sie eine Erweiterung für den machen BinaryFloatingPointTypen:

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

Einzelheiten

Xcode: 9.2, Swift 4

Lösung

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

Verwendung

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))")

Vollständige Probe

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

Ergebnisse

Geben Sie hier die Bildbeschreibung ein

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.