541 Stimmen

Wie generiere ich eine Zufallszahl in Swift?

Ich stelle fest, dass das Swift-Buch eine Implementierung eines Zufallszahlengenerators bereitgestellt hat. Ist es bewährte Praxis, diese Implementierung zu kopieren und einzufügen? Oder gibt es eine Bibliothek, die wir jetzt verwenden können?

17voto

Ryne Wang Punkte 188

@Antwort von jstn ist gut, aber ein bisschen langatmig. Swift gilt als eine sprachorientierte Sprache, daher können wir das gleiche Ergebnis erzielen, ohne für jede Klasse in der Integer-Familie Boilerplate-Code implementieren zu müssen, indem wir eine Standardimplementierung für die Protokollerweiterung hinzufügen.

public extension ExpressibleByIntegerLiteral {
    public static func arc4random() -> Self {
        var r: Self = 0
        arc4random_buf(&r, MemoryLayout.size)
        return r
    }
}

Jetzt können wir Folgendes tun:

let i = Int.arc4random()
let j = UInt32.arc4random()

und alle anderen Integer-Klassen sind in Ordnung.

14voto

Andy Jazz Punkte 34407

Aktualisiert: 09. Juni 2022.

Swift 5.7

Angenommen, wir haben ein Array:

let zahlen: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Für iOS und macOS können Sie die systemweite Zufallquelle im Xcode-Framework GameKit verwenden. Hier finden Sie die Klasse GKRandomSource mit ihrer Klassenmethode sharedRandom():

import GameKit

private func zufallsZahlGenerator() -> Int {
    let rand = GKRandomSource.sharedRandom().nextInt(upperBound: zahlen.count)
    return zahlen[rand]
}

zufallsZahlGenerator()

Sie können auch die Methode randomElement() verwenden, die ein zufälliges Element einer Sammlung zurückgibt:

let zufallsZahl = zahlen.randomElement()!
print(zufallsZahl)

Oder verwenden Sie arc4random_uniform(). Beachten Sie, dass diese Methode den Typ UInt32 zurückgibt.

let generator = Int(arc4random_uniform(11))
print(generator)

Und natürlich können wir die Methode makeIterator() verwenden, die einen Iterator über die Elemente der Sammlung zurückgibt.

let iterator: Int = (1...10).makeIterator().shuffled().first!
print(iterator)

Das letzte Beispiel, das Sie hier sehen, gibt einen zufälligen Wert innerhalb des angegebenen Bereichs mit Hilfe von static func random(in range: ClosedRange) -> Int zurück.

let zufallsZahl = Int.random(in: 1...10)
print(zufallsZahl)

Der Pseudo-zufällige Double-Zahl-Generator drand48() gibt einen Wert zwischen 0,0 und 1,0 zurück.

import Foundation

let zufallsZahl = Int(drand48() * 10)

13voto

Shehata Gamal Punkte 98978

In Swift 4.2 kannst du Zufallszahlen generieren, indem du die random()-Methode des gewünschten numerischen Typs aufrufst und den Arbeitsbereich angibst. Zum Beispiel generiert dies eine Zufallszahl im Bereich von 1 bis 9, inklusive beider Seiten

let randInt = Int.random(in: 1..<10)

Auch mit anderen Typen

let randFloat = Float.random(in: 1..<20)
let randDouble = Double.random(in: 1...30)
let randCGFloat = CGFloat.random(in: 1...40)

11voto

Jakub Truhlář Punkte 18280

Seit Swift 4.2

Es gibt einen neuen Satz von APIs:

let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomDouble = Double.random(in: 1 ... 10)
  • Alle numerischen Typen haben jetzt die random(in:) Methode, die einen Bereich nimmt.

  • Es gibt eine Zahl gleichmäßig verteilt in diesem Bereich zurück.


Zusammenfassung

Nun, was ist falsch mit dem "guten" alten Weg?

  1. Sie müssen importierte C APIs verwenden (Sie unterscheiden sich zwischen Plattformen).

  2. Und dazu...

Was ist, wenn ich Ihnen sage, dass der Zufall nicht so zufällig ist?

Wenn Sie arc4random() verwenden (um den Rest zu berechnen) wie arc4random() % eineZahl, ist das Ergebnis nicht gleichmäßig verteilt zwischen 0 und eineZahl. Es gibt ein Problem namens Modulo-Bias.

Modulo-Bias

Normalerweise generiert die Funktion eine Zufallszahl zwischen 0 und MAX (abhängig vom Typ etc.). Um ein schnelles, einfaches Beispiel zu machen, sagen wir, die maximale Zahl ist 7 und Sie interessieren sich für eine Zufallszahl im Bereich 0 ..< 2 (oder das Intervall [0, 3), wenn Sie das bevorzugen).

Die Wahrscheinlichkeiten für einzelne Zahlen sind:

  • 0: 3/8 = 37.5%
  • 1: 3/8 = 37.5%
  • 2: 2/8 = 25%

Andere Worte, du bist wahrscheinlicher, mit 0 oder 1 zu enden als mit 2. Natürlich ist zu beachten, dass dies extrem vereinfacht ist und die MAX-Zahl viel höher ist, was es fairer macht.

Dieses Problem wird durch SE-0202 - Random unification in Swift 4.2 angesprochen

9voto

demiculus Punkte 1203

Hier ist eine Bibliothek, die die Aufgabe gut erledigt https://github.com/thellimist/SwiftRandom

public extension Int {
    /// SwiftRandom Erweiterung
    public static func random(lower: Int = 0, _ upper: Int = 100) -> Int {
        return lower + Int(arc4random_uniform(UInt32(upper - lower + 1)))
    }
}

public extension Double {
    /// SwiftRandom Erweiterung
    public static func random(lower: Double = 0, _ upper: Double = 100) -> Double {
        return (Double(arc4random()) / 0xFFFFFFFF) * (upper - lower) + lower
    }
}

public extension Float {
    /// SwiftRandom Erweiterung
    public static func random(lower: Float = 0, _ upper: Float = 100) -> Float {
        return (Float(arc4random()) / 0xFFFFFFFF) * (upper - lower) + lower
    }
}

public extension CGFloat {
    /// SwiftRandom Erweiterung
    public static func random(lower: CGFloat = 0, _ upper: CGFloat = 1) -> CGFloat {
        return CGFloat(Float(arc4random()) / Float(UINT32_MAX)) * (upper - lower) + lower
    }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X