Mi rendo conto che il libro Swift ha fornito un'implementazione di un generatore di numeri casuali. È la migliore pratica copiare e incollare questa implementazione nel proprio programma? O c'è una libreria che fa questo che possiamo usare ora?
Mi rendo conto che il libro Swift ha fornito un'implementazione di un generatore di numeri casuali. È la migliore pratica copiare e incollare questa implementazione nel proprio programma? O c'è una libreria che fa questo che possiamo usare ora?
Risposte:
Swift 4.2+
Swift 4.2 fornito con Xcode 10 introduce nuove funzioni casuali facili da usare per molti tipi di dati. È possibile chiamare il random()
metodo su tipi numerici.
let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()
Utilizzare arc4random_uniform(n)
per un numero intero casuale compreso tra 0 e n-1.
let diceRoll = Int(arc4random_uniform(6) + 1)
Trasmetti il risultato a Int in modo da non dover digitare esplicitamente i tuoi parametri come UInt32
(che sembra non Swifty).
0
. Nel tuo codice, diceRoll
potrebbe essere 0
. Sto solo dicendo ...
Int(arc4random_uniform(6)+1)
.
arc3random_uniform(n)
a UInt32(n)
se stai utilizzando un valore che non è già di quel tipo.
Modifica: aggiornato per Swift 3.0
arc4random
funziona bene in Swift, ma le funzioni di base sono limitate ai tipi interi a 32 bit (Int
è a 64 bit su iPhone 5S e Mac moderni). Ecco una funzione generica per un numero casuale di un tipo esprimibile da un intero letterale:
public func arc4random<T: ExpressibleByIntegerLiteral>(_ type: T.Type) -> T {
var r: T = 0
arc4random_buf(&r, MemoryLayout<T>.size)
return r
}
Possiamo usare questa nuova funzione generica per estendere UInt64
, aggiungere argomenti al contorno e mitigare la distorsione del modulo. (Viene sollevato direttamente da 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
}
}
Con ciò possiamo estendere Int64
gli stessi argomenti, trattando l'overflow:
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
}
}
}
Per completare la famiglia ...
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
}
}
}
Dopo tutto ciò, possiamo finalmente fare qualcosa del genere:
let diceRoll = UInt64.random(lower: 1, upper: 7)
var r = arc4random(UInt64)
. Per favore, cosa intendi qui?
arc4random
(definita nel primo blocco di codice) con l'argomento UInt64
che è a Type
.
arc4random_buf
. Lo scopo di queste estensioni è fare esattamente ciò che arc4random_uniform
fa (mitigare il bias del modulo) ad eccezione dei tipi a 64 bit.
Modifica per Swift 4.2
A partire da Swift 4.2, invece di utilizzare la funzione C importata arc4random_uniform (), ora puoi utilizzare le funzioni native di Swift.
// Generates integers starting with 0 up to, and including, 10
Int.random(in: 0 ... 10)
Puoi usare random(in:)
per ottenere valori casuali anche per altri valori primitivi; come Int, Double, Float e persino Bool.
Versioni veloci <4.2
Questo metodo genererà un Int
valore casuale tra il minimo e il massimo indicati
func randomInt(min: Int, max: Int) -> Int {
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
Ho usato questo codice:
var k: Int = random() % 10;
random is unavailable in Swift: Use arc4random instead.
A partire da iOS 9, puoi utilizzare le nuove classi GameplayKit per generare numeri casuali in vari modi.
Puoi scegliere tra quattro tipi di sorgente: una fonte casuale generale (senza nome, fino al sistema per scegliere cosa fare), lineare congruenziale, ARC4 e Mersenne Twister. Questi possono generare ints, float e bool casuali.
Al livello più semplice, è possibile generare un numero casuale dalla fonte casuale incorporata del sistema in questo modo:
GKRandomSource.sharedRandom().nextInt()
Ciò genera un numero compreso tra -2.147.483.648 e 2.147.483.647. Se si desidera un numero compreso tra 0 e un limite superiore (esclusivo), utilizzare questo:
GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
GameplayKit ha alcuni costruttori di convenienza integrati per lavorare con i dadi. Ad esempio, puoi tirare un dado a sei facce in questo modo:
let d6 = GKRandomDistribution.d6()
d6.nextInt()
Inoltre puoi modellare la distribuzione casuale usando cose come GKShuffledDistribution. Questo richiede un po 'più di spiegazione, ma se sei interessato puoi leggere il mio tutorial su GameplayKit numeri casuali .
import GameplayKit
. Swift 3 ha cambiato la sintassi inGKRandomSource.sharedRandom().nextInt(upperBound: 6)
Puoi farlo nello stesso modo in cui lo faresti in C:
let randomNumber = arc4random()
randomNumber
viene considerato di tipo UInt32
(un numero intero senza segno a 32 bit)
rand
, arc4random
, drand48
e gli amici sono tutti nel Darwin
modulo. È già importato per te se stai costruendo un'app Cocoa, UIKit o Foundation, ma dovrai farlo import Darwin
nei parchi giochi.
arc4random_uniform()
Uso:
arc4random_uniform(someNumber: UInt32) -> UInt32
Questo ti dà numeri casuali nell'intervallo 0
a someNumber - 1
.
Il valore massimo per UInt32
è 4.294.967.295 (ovvero, 2^32 - 1
).
Esempi:
Testa o croce
let flip = arc4random_uniform(2) // 0 or 1
Rotolo di dadi
let roll = arc4random_uniform(6) + 1 // 1...6
Giornata casuale ad ottobre
let day = arc4random_uniform(31) + 1 // 1...31
Anno casuale negli anni '90
let year = 1990 + arc4random_uniform(10)
Forma generale:
let number = min + arc4random_uniform(max - min + 1)
dove number
, max
e min
sono UInt32
.
arc4random ()
Puoi anche ottenere un numero casuale usando arc4random()
, che produce UInt32
tra 0 e 2 ^ 32-1. Pertanto, per ottenere un numero casuale tra 0
e x-1
, puoi dividerlo per x
e prendere il resto. O in altre parole, utilizzare l' operatore Remainder (%) :
let number = arc4random() % 5 // 0...4
Tuttavia, questo produce una leggera distorsione del modulo (vedi anche qui e qui ), quindi è per questo che arc4random_uniform()
è raccomandato.
Conversione da e verso Int
Normalmente sarebbe bene fare qualcosa del genere per convertire avanti e indietro tra Int
e UInt32
:
let number: Int = 10
let random = Int(arc4random_uniform(UInt32(number)))
Il problema, tuttavia, è che Int
ha una gamma di -2,147,483,648...2,147,483,647
sistemi a 32 bit e una gamma di -9,223,372,036,854,775,808...9,223,372,036,854,775,807
sistemi a 64 bit. Confronta questo con la UInt32
gamma di 0...4,294,967,295
. Il U
di UInt32
significa non firmato .
Considera i seguenti errori:
UInt32(-1) // negative numbers cause integer overflow error
UInt32(4294967296) // numbers greater than 4,294,967,295 cause integer overflow error
Quindi devi solo essere sicuro che i tuoi parametri di input siano all'interno UInt32
dell'intervallo e che non ti serva nemmeno un output al di fuori di tale intervallo.
Sono stato in grado di utilizzare solo rand()
per ottenere un CInt casuale. Puoi renderlo un Int usando qualcosa del genere:
let myVar: Int = Int(rand())
Puoi usare la tua funzione casuale C preferita e, se necessario, convertire semplicemente in valore in Int.
random()
, che restituisce un Int
piuttosto che UInt32
- e come menzionato da @SomeGuy, semplicemente chiama srandom(arc4random())
una volta ovunque prima di usarlo per assicurarti che abbia un seme diverso e randomizzato per ogni esecuzione del tuo programma.
La risposta di @ jstn è buona, ma un po 'dettagliata. Swift è noto come linguaggio orientato al protocollo, quindi possiamo ottenere lo stesso risultato senza dover implementare il codice boilerplate per ogni classe nella famiglia di interi, aggiungendo un'implementazione predefinita per l'estensione del protocollo.
public extension ExpressibleByIntegerLiteral {
public static func arc4random() -> Self {
var r: Self = 0
arc4random_buf(&r, MemoryLayout<Self>.size)
return r
}
}
Ora possiamo fare:
let i = Int.arc4random()
let j = UInt32.arc4random()
e tutte le altre classi di numeri interi sono ok.
In Swift 4.2 è possibile generare numeri casuali chiamando il random()
metodo su qualsiasi tipo numerico desiderato, fornendo l'intervallo con cui si desidera lavorare. Ad esempio, questo genera un numero casuale nell'intervallo da 1 a 9, incluso su entrambi i lati
let randInt = Int.random(in: 1..<10)
Anche con altri tipi
let randFloat = Float.random(in: 1..<20)
let randDouble = Double.random(in: 1...30)
let randCGFloat = CGFloat.random(in: 1...40)
Ecco una libreria che fa bene il lavoro 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
}
}
let MAX : UInt32 = 9
let MIN : UInt32 = 1
func randomNumber()
{
var random_number = Int(arc4random_uniform(MAX) + MIN)
print ("random = ", random_number);
}
Vorrei aggiungere alle risposte esistenti che l'esempio di generatore di numeri casuali nel libro di Swift è un generatore di congruenza lineare (LCG), è un modello fortemente limitato e non dovrebbe essere, tranne per gli esempi banali, in cui la qualità della casualità non non importa affatto. E un LCG non dovrebbe mai essere usato per scopi crittografici .
arc4random()
è molto meglio e può essere utilizzato per la maggior parte degli scopi, ma di nuovo non dovrebbe essere usato per scopi crittografici.
Se vuoi qualcosa che sia garantito crittograficamente sicuro, usa SecCopyRandomBytes()
. Nota che se costruisci un generatore di numeri casuali in qualcosa, qualcun altro potrebbe finire (mis) usandolo per scopi crittografici (come password, generazione di chiavi o sale), allora dovresti considerare di usare SecCopyRandomBytes()
comunque, anche se le tue necessità non lo fanno ' Non è assolutamente necessario.
Esiste un nuovo set di API:
let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomDouble = Double.random(in: 1 ... 10)
Tutti i tipi numerici ora hanno il random(in:)
metodo che richiede range
.
Restituisce un numero uniformemente distribuito in quell'intervallo.
TL; DR
Devi usare le API C importate (sono diverse tra le piattaforme) .
Ed inoltre...
E se ti dicessi che il casuale non è così casuale?
Se si utilizza arc4random()
(per calcolare il resto) come arc4random() % aNumber
, il risultato non viene distribuito uniformemente tra 0
e aNumber
. Si è verificato un problema chiamato distorsione modulo .
Distorsione da modulo
Normalmente, la funzione genera un numero casuale compreso tra 0
e MAX (dipende dal tipo ecc . ) . Per fare un esempio semplice e veloce, supponiamo che il numero massimo sia 7
e ti preoccupi di un numero casuale nell'intervallo 0 ..< 2
(o nell'intervallo [0, 3) se lo preferisci) .
Le probabilità per i singoli numeri sono:
In altre parole, è più probabile che finisca con 0 o 1 rispetto a 2 . Naturalmente, tieni presente che questo è estremamente semplificato e il numero MAX è molto più alto, rendendolo più "equo".
Questo problema viene risolto da SE-0202 - Unificazione casuale in Swift 4.2
Senza arc4Random_uniform () in alcune versioni di Xcode (in 7.1 funziona ma non si completa automaticamente per me). Puoi farlo invece.
Per generare un numero casuale da 0-5. Primo
import GameplayKit
Poi
let diceRoll = GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
var randomNumber = Int(arc4random_uniform(UInt32(5)))
Qui 5 si assicurerà che il numero casuale sia generato da zero a quattro. È possibile impostare il valore di conseguenza.
Rapido 4.2
Arrivederci per importare 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()
Il seguente codice produrrà un numero casuale sicuro compreso tra 0 e 255:
extension UInt8 {
public static var random: UInt8 {
var number: UInt8 = 0
_ = SecRandomCopyBytes(kSecRandomDefault, 1, &number)
return number
}
}
Lo chiami così:
print(UInt8.random)
Per numeri più grandi diventa più complicato.
Questo è il meglio che ho potuto inventare:
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) }
}
}
Questi metodi utilizzano un numero casuale aggiuntivo per determinare quanti UInt8
s verranno utilizzati per creare il numero casuale. L'ultima riga converte [UInt8]
in UInt16
oUInt32
.
Non so se gli ultimi due contano ancora come casuali, ma puoi modificarlo a tuo piacimento :)
Rapido 4.2
Swift 4.2 ha incluso un'API per numeri casuali nativa e abbastanza completa nella libreria standard. ( Proposta Swift Evolution SE-0202 )
let intBetween0to9 = Int.random(in: 0...9)
let doubleBetween0to1 = Double.random(in: 0...1)
Tutti i tipi di numero hanno il random statico (in :) che accetta l'intervallo e restituisce il numero casuale nell'intervallo specificato
Swift 4.2, Xcode 10.1 .
Per iOS, macOS e tvOS è possibile utilizzare l'origine casuale a livello di sistema nel framework di Xcode GameKit
. Qui puoi trovare la GKRandomSource
classe con il suo sharedRandom()
metodo class:
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()
O semplicemente usa un randomElement()
metodo che restituisce un elemento casuale della raccolta:
let number: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let randomNumber = number.randomElement()!
print(randomNumber)
Puoi usare GeneratorOf
così:
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())
Uso questo codice per generare un numero casuale:
//
// 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]
}
}
xCode 9.1, Swift 4
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
}
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]
Non dimenticare di aggiungere qui il codice della soluzione orientata alla matematica (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]
}
}
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]
Non dimenticare di aggiungere qui i codici soluzione (1) e soluzione (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]
}