¿Cómo se genera un número aleatorio en el lenguaje Swift de Apple?

Me doy cuenta de que el libro de Swift proporciona una implementación de un generador de números aleatorios. ¿Es la mejor práctica copiar y pegar esta implementación en el propio progtwig? ¿O hay una biblioteca que hace esto que podemos usar ahora?

Utilice las funciones de biblioteca estándar para números aleatorios de alta calidad: arc4random() o arc4random_uniform() , como en Objective-C.

Están en el módulo Darwin , por lo que si no ha importado UIKit , UIKit o Foundation (que lo importan), tendrá que import Darwin .

Swift 4.2

Swift 4.2 incluido con Xcode 10 presenta 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() 

Utilice arc4random_uniform(n) para un entero aleatorio entre 0 y n-1.

 let diceRoll = Int(arc4random_uniform(6) + 1) 

Transmite el resultado a Int para que no tengas que escribir explícitamente tus vars como UInt32 (que parece no Swifty).

Editar: actualizado para Swift 3.0

arc4random funciona bien en Swift, pero las funciones base están limitadas a tipos enteros de 32 bits ( Int es de 64 bits en el iPhone 5S y los Mac modernos). Aquí hay una función genérica para un número aleatorio de un tipo expresable por un literal entero:

 public func arc4random(_ type: T.Type) -> T { var r: T = 0 arc4random_buf(&r, MemoryLayout.size) return r } 

Podemos usar esta nueva función genérica para extender UInt64 , agregando argumentos de límite y atenuando el sesgo de módulo. (Esto se saca 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, que se ocupan del 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) 

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

Usé este código:

 var k: Int = random() % 10; 

A partir de iOS 9, puede usar las nuevas clases GameplayKit para generar números aleatorios de varias maneras.

Usted tiene cuatro tipos de fonts para elegir: una fuente aleatoria general (sin nombre, hasta el sistema para elegir lo que hace), congruencia lineal, ARC4 y Twister Mersenne. Estos pueden generar random ints, floats y bools.

En el nivel más simple, puede generar un número aleatorio a partir de la fuente aleatoria integrada del sistema como esta:

 GKRandomSource.sharedRandom().nextInt() 

Eso genera un número entre -2,147,483,648 y 2,147,483,647. Si quieres un número entre 0 y un límite superior (exclusivo) deberías usar esto:

 GKRandomSource.sharedRandom().nextIntWithUpperBound(6) 

GameplayKit tiene algunos constructores de conveniencia integrados para trabajar con dados. Por ejemplo, puedes lanzar un dado de seis caras como este:

 let d6 = GKRandomDistribution.d6() d6.nextInt() 

Además, puedes dar forma a la distribución aleatoria usando cosas como GKShuffledDistribution. Eso requiere un poco más de explicación, pero si estás interesado puedes leer mi tutorial en GameplayKit números aleatorios .

Puedes hacerlo de la misma manera que lo harías en C:

 let randomNumber = arc4random() 

randomNumber se infiere como de tipo UInt32 (un entero sin signo de 32 bits)

Use 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 la década de 1990

     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 usando 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 rest. O en otras palabras, use el Operador Restante (%) :

 let number = arc4random() % 5 // 0...4 

Sin embargo, esto produce un ligero sesgo de módulo (ver también aquí y aquí ), por lo que se arc4random_uniform() .

Convirtiendo hacia y desde Int

Normalmente, estaría bien hacer algo como esto para convertir entre Int y UInt32 :

 let number: Int = 10 let random = Int(arc4random_uniform(UInt32(number))) 

El problema, sin embargo, 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 unsigned .

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 de que no necesite una salida que esté fuera de ese rango tampoco.

Ejemplo para un número aleatorio entre 10 (0-9);

 import UIKit let randomNumber = Int(arc4random_uniform(10)) 

Código muy sencillo: simple y corto.

Solo he podido usar rand() para obtener un CInt aleatorio. Puede hacer que sea un Int usando algo como esto:

 let myVar: Int = Int(rand()) 

Puede usar su función aleatoria C favorita y simplemente convertir a valor a Int si es necesario.

La respuesta de @ jstn es buena, pero un poco prolija. Swift es conocido como un lenguaje orientado al protocolo, por lo que podemos lograr el mismo resultado sin tener que implementar un código repetitivo para cada clase en la familia entera, 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.size) return r } } 

Ahora podemos hacer:

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

y todas las otras clases enteras están bien.

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

En Swift 4.2 puede generar números aleatorios llamando al método random() en cualquier 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, inclusive en ambos lados

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

También con otros tipos

 let randFloat = Float.random(in: 1..<20) let randDouble = Double.random(in: 1...30) let randCGFloat = CGFloat.random(in: 1...40) 
  let MAX : UInt32 = 9 let MIN : UInt32 = 1 func randomNumber() { var random_number = Int(arc4random_uniform(MAX) + MIN) print ("random = ", random_number); } 

Me gustaría agregar a las respuestas existentes que el ejemplo de generador de números aleatorios en el libro de Swift es un Generador de congruencia lineal (LCG), es muy limitado y no debe ser, excepto para los ejemplos triviales, donde la calidad de la aleatoriedad no no importa en absoluto. Y un LCG nunca debe usarse con fines criptográficos .

arc4random() es mucho mejor y se puede usar para la mayoría de los propósitos, pero nuevamente no se debe usar con fines criptográficos.

Si desea algo que garantice la seguridad criptográfica, use SecCopyRandomBytes() . Tenga en cuenta que si crea un generador de números aleatorios en algo, alguien más puede terminar (mal) -utilizándolo con fines criptográficos (como contraseña, clave o generación de sal), entonces debe considerar usar SecCopyRandomBytes() todos modos, incluso si su la necesidad no requiere eso.

 var randomNumber = Int(arc4random_uniform(UInt32(**5**))) 

Aquí 5 se asegurará de que se genere el número aleatorio a pesar de cero a cinco. Puede establecer el valor en consecuencia.

Sin arc4Random_uniform () en algunas versiones de Xcode (en 7.1 se ejecuta pero no se autocompleta). 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) 

Desde Swift 4.2

Hay un nuevo conjunto de API:

 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 toma el range .

  • Devuelve un número distribuido uniformemente en ese rango.


TL; DR

Bueno, ¿qué está mal con el "buen" viejo modo?

  1. Debe usar API C importadas (son diferentes entre plataformas) .

  2. Y además...

¿Qué pasa si te digo que el azar no es tan aleatorio?

Si usa arc4random() (para calcular el rest) como arc4random() % aNumber , el resultado no se distribuye uniformemente entre 0 y aNumber . Hay un problema llamado sesgo Modulo .

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 prefieres eso] .

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 termines con 0 o 1 que 2 . Por supuesto, hay que tener en cuenta que esto es extremadamente simplificado y que el número MAX es mucho más alto, por lo que es más "justo".

Este problema es abordado por SE-0202 - Unificación aleatoria en Swift 4.2

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 pude hacer:

 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 usan 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 realmente aleatorios, pero puedes ajustarlo a tus gustos 🙂

Swift 4.2

Swift 4.2 ha incluido una API de número aleatorio nativo y bastante completo en la biblioteca estándar. ( Propuesta de Swift Evolution SE-0202 )

 let intBetween0to9 = Int.random(in: 0...9) let doubleBetween0to1 = Double.random(in: 0...1) 

Todos los tipos de números tienen el estático aleatorio (en 🙂 que toma el rango y devuelve el número aleatorio en el rango dado

Detalles

xCode 9.1, Swift 4

Solución orientada a las matemáticas (1)

 import Foundation class Random { subscript(_ min: T, _ max: T) -> T where T : BinaryInteger { get { return rand(min-1, max+1) } } } let rand = Random() func rand(_ 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 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 progtwigdores (2)

No olvides agregar el código de solución matemática orientada (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 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() 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

enter image description here

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

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

Para iOS, macOS y tvOS puede usar fonts aleatorias de todo el sistema en el marco de trabajo de Xcode GameKit . 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] } 

Está escrito en Swift 4.2.