programacion - ¿Cómo se genera un número aleatorio en el lenguaje Swift de Apple?
swift lenguaje de programacion caracteristicas (25)
Swift 4.2, Xcode 10.1 .
Para iOS, macOS y tvOS puede usar una fuente aleatoria de todo el sistema en el framework GameKit
Xcode. Aquí puede encontrar la clase GKRandomSource
con su método de clase sharedRandom()
:
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 simplemente use un método randomElement()
que devuelva un elemento aleatorio de la colección:
let number: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let randomNumber = number.randomElement()!
print(randomNumber)
Me doy cuenta de que el libro Swift proporcionó una implementación de un generador de números aleatorios. ¿Es la mejor práctica copiar y pegar esta implementación en el programa propio? ¿O hay una biblioteca que hace esto que podemos usar ahora?
Desde Swift 4.2
Hay un nuevo conjunto de APIs:
let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomDouble = Double.random(in: 1 ... 10)
Todos los tipos numéricos ahora tienen el método
random(in:)
que tomarange
.Devuelve un número distribuido uniformemente en ese rango.
TL; DR
Bueno, ¿qué hay de malo con la "buena" forma antigua?
Tienes que usar las API C importadas (son diferentes entre plataformas) .
Y además...
¿Qué pasa si te digo que el azar no es tan aleatorio?
Si usa arc4random()
(para calcular el resto) como arc4random() % aNumber
, el resultado no se distribuye de manera uniforme entre 0
y aNumber
. Hay un problema llamado el sesgo de módulo .
Modulo sesgo
Normalmente, la función genera un número aleatorio entre 0
y MAX (depende del tipo, etc.) . Para hacer un ejemplo rápido y fácil, digamos que el número máximo es 7
y te importa un número aleatorio en el rango 0 ..< 2
(o el intervalo [0, 3) si lo prefieres) .
Las probabilidades para números individuales son:
- 0: 3/8 = 37.5%
- 1: 3/8 = 37.5%
- 2: 2/8 = 25%
En otras palabras, es más probable que termine con 0 o 1 que 2 . Por supuesto, tenga en cuenta que esto es extremadamente simplificado y que el número MAX es mucho más alto, lo que lo hace más "justo".
Este problema es resuelto por SE-0202 - Unificación aleatoria en Swift 4.2
Utilice arc4random_uniform()
Uso:
arc4random_uniform(someNumber: UInt32) -> UInt32
Esto le da enteros aleatorios en el rango de 0
a someNumber - 1
.
El valor máximo para UInt32
es 4,294,967,295 (es decir, 2^32 - 1
).
Ejemplos:
Lanzamiento de moneda
let flip = arc4random_uniform(2) // 0 or 1
Tirada de dados
let roll = arc4random_uniform(6) + 1 // 1...6
Día aleatorio en octubre
let day = arc4random_uniform(31) + 1 // 1...31
Año aleatorio en los años 90.
let year = 1990 + arc4random_uniform(10)
Forma general:
let number = min + arc4random_uniform(max - min + 1)
donde number
, max
y min
son UInt32
.
Qué pasa...
arc4random ()
También puede obtener un número aleatorio utilizando arc4random()
, que produce un UInt32
entre 0 y 2 ^ 32-1. Por lo tanto, para obtener un número aleatorio entre 0
y x-1
, puede dividirlo por x
y tomar el resto. O, en otras palabras, utilice el operador de resto (%) :
let number = arc4random() % 5 // 0...4
Sin embargo, esto produce un ligero sesgo de módulo (consulte también here y here ), por lo que se arc4random_uniform()
.
Convertir hacia y desde Int
Normalmente estaría bien hacer algo como esto para convertir de un lado a otro entre Int
y UInt32
:
let number: Int = 10
let random = Int(arc4random_uniform(UInt32(number)))
No obstante, el problema es que Int
tiene un rango de -2,147,483,648...2,147,483,647
en sistemas de 32 bits y un rango de -9,223,372,036,854,775,808...9,223,372,036,854,775,807
en sistemas de 64 bits. Compare esto con el rango UInt32
de 0...4,294,967,295
. La U
de UInt32
significa sin firmar .
Considere los siguientes errores:
UInt32(-1) // negative numbers cause integer overflow error
UInt32(4294967296) // numbers greater than 4,294,967,295 cause integer overflow error
Por lo tanto, solo debe asegurarse de que sus parámetros de entrada estén dentro del rango UInt32
y que tampoco necesita una salida que esté fuera de ese rango.
Detalles
xCode 9.1, Swift 4
Solución orientada a las matemáticas (1)
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
}
Uso de la solución (1)
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]
Solución orientada a programadores (2)
No olvide agregar el código de la solución orientada a matemáticas (1) aquí
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]
}
}
Uso de la solución (2)
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]
Muestra completa
No olvide agregar los códigos de solución (1) y solución (2) aquí
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]
}
Resultado de la muestra
A partir de iOS 9, puedes usar las nuevas clases GameplayKit para generar números aleatorios de varias maneras.
Tiene cuatro tipos de fuentes para elegir: una fuente aleatoria general (sin nombre, hasta el sistema para elegir lo que hace), congruente lineal, ARC4 y Mersenne Twister. Estos pueden generar entradas aleatorias, flotadores y bools.
En el nivel más simple, puede generar un número aleatorio a partir de la fuente aleatoria incorporada del sistema de esta manera:
GKRandomSource.sharedRandom().nextInt()
Eso genera un número entre -2,147,483,648 y 2,147,483,647. Si desea un número entre 0 y un límite superior (exclusivo), use este:
GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
GameplayKit tiene algunos constructores de conveniencia integrados para trabajar con dados. Por ejemplo, puedes tirar un dado de seis caras como este:
let d6 = GKRandomDistribution.d6()
d6.nextInt()
Además, puede configurar la distribución aleatoria utilizando elementos como GKShuffledDistribution. Eso requiere un poco más de explicación, pero si estás interesado, puedes leer mi tutorial sobre números aleatorios de GameplayKit .
Aquí hay una biblioteca que hace bien el trabajo 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
}
}
Ejemplo para número aleatorio entre 10 (0-9);
import UIKit
let randomNumber = Int(arc4random_uniform(10))
Código muy fácil - simple y corto.
El siguiente código producirá un número aleatorio seguro entre 0 y 255:
extension UInt8 {
public static var random: UInt8 {
var number: UInt8 = 0
_ = SecRandomCopyBytes(kSecRandomDefault, 1, &number)
return number
}
}
Lo llamas así:
print(UInt8.random)
Para números más grandes se vuelve más complicado.
Esto es lo mejor que se me ocurre:
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) }
}
}
Estos métodos utilizan un número aleatorio adicional para determinar cuántos UInt8
se usarán para crear el número aleatorio. La última línea convierte el [UInt8]
a UInt16
o UInt32
.
No sé si los dos últimos todavía cuentan como verdaderamente aleatorios, pero puedes modificarlos a tu gusto :)
En Swift 4.2 puede generar números aleatorios llamando al método random()
en el tipo numérico que desee, proporcionando el rango con el que desea trabajar. Por ejemplo, esto genera un número aleatorio en el rango de 1 a 9, ambos lados incluidos
let randInt = Int.random(in: 1..<10)
Tambien con otros tipos
let randFloat = Float.random(in: 1..<20)
let randDouble = Double.random(in: 1...30)
let randCGFloat = CGFloat.random(in: 1...40)
He podido usar simplemente rand()
para obtener un CIT aleatorio. Puedes convertirlo en un Int usando algo como esto:
let myVar: Int = Int(rand())
Puede usar su función aleatoria favorita de C, y simplemente convertir a valor a Int si es necesario.
Me gustaría agregar a las respuestas existentes que el ejemplo del generador de números aleatorios en el libro Swift es un generador de congruencia lineal (LCG), es muy limitado y no debería serlo, excepto los ejemplos triviales, donde la calidad de la aleatoriedad no no importa en absoluto Y una LCG nunca debe utilizarse para fines criptográficos .
arc4random()
es mucho mejor y puede usarse para la mayoría de los propósitos, pero nuevamente no debe usarse para propósitos criptográficos.
Si desea algo que esté garantizado para ser criptográficamente seguro, use SecCopyRandomBytes()
. Tenga en cuenta que si integra un generador de números aleatorios en algo, otra persona podría terminar de SecCopyRandomBytes()
fines criptográficos (como contraseña, clave o generación de sal), entonces debería considerar usar SecCopyRandomBytes()
todos modos, incluso si su la necesidad no requiere eso.
Puedes hacerlo de la misma manera que lo harías en C:
let randomNumber = arc4random()
se deduce que randomNumber
es del tipo UInt32
(un entero sin signo de 32 bits)
Puedes usar GeneratorOf
así:
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())
Sin arc4Random_uniform () en algunas versiones de Xcode (en 7.1 se ejecuta pero no se completa automáticamente). Puedes hacer esto en su lugar.
Para generar un número aleatorio de 0-5. primero
import GameplayKit
Entonces
let diceRoll = GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
Utilicé este código:
var k: Int = random() % 10;
Utilice arc4random_uniform(n)
para un entero aleatorio entre 0 y n-1.
let diceRoll = Int(arc4random_uniform(6) + 1)
Envía el resultado a Int para que no tengas que escribir explícitamente tus vars como UInt32
(lo que parece no-Swifty).
Yo uso este código para generar un número aleatorio:
//
// 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]
}
}
Edición: Actualizado para Swift 3.0
arc4random
funciona bien en Swift, pero las funciones básicas están limitadas a tipos enteros de 32 bits ( Int
es de 64 bits en iPhone 5S y Macs modernos). Aquí hay una función genérica para un número aleatorio de un tipo expresable por un literal entero:
public func arc4random<T: ExpressibleByIntegerLiteral>(_ type: T.Type) -> T {
var r: T = 0
arc4random_buf(&r, MemoryLayout<T>.size)
return r
}
Podemos usar esta nueva función genérica para extender UInt64
, agregando argumentos de límites y mitigando el sesgo de módulo. (Esto se levanta directamente de 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 eso podemos extender Int64
para los mismos argumentos, tratando con el desbordamiento:
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
}
}
}
Para completar la familia ...
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
}
}
}
Después de todo eso, finalmente podemos hacer algo como esto:
let diceRoll = UInt64.random(lower: 1, upper: 7)
Editar para Swift 4.2
A partir de Swift 4.2, en lugar de usar la función C importada arc4random_uniform (), ahora puede usar las funciones nativas de Swift.
// Generates integers starting with 0 up to, and including, 10
Int.random(in: 0 ... 10)
Puede usar random(in:)
para obtener valores aleatorios para otros valores primitivos también; como Int, Double, Float e incluso Bool.
Versiones Swift <4.2
Este método generará un valor Int
aleatorio entre el mínimo y el máximo dados.
func randomInt(min: Int, max: Int) -> Int {
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
Swift 4.2
Adiós para importar 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()
- Usas aleatorio (en :) para generar dígitos aleatorios de rangos.
- randomElement () devuelve nil si el rango está vacío, ¿así que desenvuelve el Int devuelto? con si se deja
- Utiliza random (in :) para generar un Double, Float o CGFloat aleatorio y random () para devolver un Bool aleatorio.
Swift 4.2
Swift 4.2 ha incluido un API de número aleatorio nativo y bastante completo en la biblioteca estándar. ( Swift Evolution propuesta SE-0202 )
let intBetween0to9 = Int.random(in: 0...9)
let doubleBetween0to1 = Double.random(in: 0...1)
Todos los tipos de números tienen el random(in:) estático random(in:) que toma el rango y devuelve el número aleatorio en el rango dado
La respuesta de @jstn es buena, pero un poco verbosa. Swift se conoce como un lenguaje orientado al protocolo, por lo que podemos lograr el mismo resultado sin tener que implementar el código de repetición para cada clase en la familia de enteros, agregando una implementación predeterminada para la extensión del protocolo.
public extension ExpressibleByIntegerLiteral {
public static func arc4random() -> Self {
var r: Self = 0
arc4random_buf(&r, MemoryLayout<Self>.size)
return r
}
}
Ahora podemos hacer:
let i = Int.arc4random()
let j = UInt32.arc4random()
y todas las demás clases de enteros están bien.
Utilice las funciones de la biblioteca estándar para los números aleatorios de alta calidad: arc4random()
o arc4random_uniform()
, al igual que en Objective-C.
Están en el módulo de Darwin
, por lo que si no ha importado UIKit
, UIKit
o Foundation
(que lo importa para usted), deberá import Darwin
.
Swift 4.2
Swift 4.2 incluido con Xcode 10 introduce nuevas funciones aleatorias fáciles de usar para muchos tipos de datos. Puede llamar al método random()
en tipos numéricos.
let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()
let MAX : UInt32 = 9
let MIN : UInt32 = 1
func randomNumber()
{
var random_number = Int(arc4random_uniform(MAX) + MIN)
print ("random = ", random_number);
}
var randomNumber = Int(arc4random_uniform(UInt32(**5**)))
Aquí 5 se asegurará de que el número aleatorio se genere de cero a cinco. Puede establecer el valor en consecuencia.