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 propio programa? ¿O hay una biblioteca que hace esto que podemos usar ahora?
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 propio programa? ¿O hay una biblioteca que hace esto que podemos usar ahora?
Respuestas:
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 random()
método en tipos numéricos.
let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()
Úselo arc4random_uniform(n)
para un entero aleatorio entre 0 y n-1.
let diceRoll = Int(arc4random_uniform(6) + 1)
Transmita el resultado a Int para que no tenga que escribir explícitamente sus vars como UInt32
(lo que parece no Swifty).
0
. En su código, diceRoll
podría ser 0
. Solo digo ...
Int(arc4random_uniform(6)+1)
.
arc3random_uniform(n)
a UInt32(n)
si está utilizando un valor que aún no es de ese tipo.
Editar: 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
, agregar argumentos de límite y mitigar 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 extendernos Int64
a los mismos argumentos, lidiando 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)
var r = arc4random(UInt64)
. Por favor consejo, ¿qué quisiste decir aquí?
arc4random
(definida en el primer bloque de código) con el argumento UInt64
que es a Type
.
arc4random_buf
. El propósito de estas extensiones es hacer exactamente lo que arc4random_uniform
hace (mitigar el sesgo de módulo) excepto para los tipos de 64 bits.
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)
También puede usar random(in:)
para obtener valores aleatorios para otros valores primitivos; como Int, Double, Float e incluso Bool.
Versiones Swift <4.2
Este método generará un Int
valor 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;
random is unavailable in Swift: Use arc4random instead.
A partir de iOS 9, puedes usar las nuevas clases de GameplayKit para generar números aleatorios de varias maneras.
Tiene cuatro tipos de fuente para elegir: una fuente aleatoria general (sin nombre, hasta el sistema para elegir lo que hace), lineal congruencial, ARC4 y Mersenne Twister. Estos pueden generar entradas aleatorias, flotantes 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) usaría esto:
GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
GameplayKit tiene algunos constructores convenientes integrados para trabajar con dados. Por ejemplo, puedes tirar un dado de seis lados como este:
let d6 = GKRandomDistribution.d6()
d6.nextInt()
Además, puede dar forma a 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 los números aleatorios de GameplayKit .
import GameplayKit
. Swift 3 cambió la sintaxis aGKRandomSource.sharedRandom().nextInt(upperBound: 6)
Puede hacerlo de la misma manera que lo haría en C:
let randomNumber = arc4random()
randomNumber
se infiere que es de tipo UInt32
(un entero sin signo de 32 bits)
rand
, arc4random
, drand48
y amigos están todos en el Darwin
módulo. Ya se importó para usted si está creando una aplicación Cocoa, UIKit o Foundation, pero lo necesitará import Darwin
en los parques infantiles.
arc4random_uniform()
Uso:
arc4random_uniform(someNumber: UInt32) -> UInt32
Esto le da enteros aleatorios en el rango 0
de someNumber - 1
.
El valor máximo para UInt32
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
están UInt32
.
arc4random ()
También puede obtener un número aleatorio mediante el uso arc4random()
, que produce UInt32
entre 0 y 2 ^ 32-1. Por lo tanto, para obtener un número aleatorio entre 0
y x-1
, puede dividirlo entre x
y tomar el resto. 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 eso arc4random_uniform()
se recomienda.
Conversión ay desde Int
Normalmente estaría bien hacer algo como esto para convertir de ida y vuelta entre Int
y UInt32
:
let number: Int = 10
let random = Int(arc4random_uniform(UInt32(number)))
Sin embargo, el problema es que Int
tiene un rango de -2,147,483,648...2,147,483,647
sistemas de 32 bits y un rango de -9,223,372,036,854,775,808...9,223,372,036,854,775,807
sistemas de 64 bits. Compare esto con el UInt32
rango de 0...4,294,967,295
. El 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 UInt32
rango y que tampoco necesite una salida que esté fuera de ese rango.
Solo he podido usar rand()
para obtener un CI aleatorio. Puedes convertirlo en Int usando algo como esto:
let myVar: Int = Int(rand())
Puede usar su función aleatoria C favorita y simplemente convertirla en valor a Int si es necesario.
random()
, que devuelve un en Int
lugar de UInt32
, y como @SomeGuy mencionó, simplemente llame srandom(arc4random())
una vez en cualquier lugar antes de usarlo para asegurarse de que tenga una semilla aleatoria diferente para cada ejecución de su programa.
La respuesta de @ jstn es buena, pero un poco detallada. Swift se conoce 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 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.
En Swift 4.2 puede generar números aleatorios llamando al random()
método 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)
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
}
}
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 del generador de números aleatorios en el libro de Swift es un Generador de congruencia lineal (LCG), es muy limitado y no debería ser excepto por 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 puede usarse para la mayoría de los propósitos, pero nuevamente no debe usarse para propósitos criptográficos.
Si desea algo que garantice su seguridad criptográfica, úselo SecCopyRandomBytes()
. Tenga en cuenta que si construye un generador de números aleatorios en algo, alguien más podría terminar (mal) usándolo con fines criptográficos (como contraseña, clave o generación de sal), entonces debería considerar usarlo de SecCopyRandomBytes()
todos modos, incluso si su necesidad no lo hace. No es necesario eso.
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 random(in:)
método que toma range
.
Devuelve un número uniformemente distribuido en ese rango.
TL; DR
Debe usar las API de C importadas (son diferentes entre plataformas) .
Y además...
¿Qué pasa si te digo que lo aleatorio no es tan aleatorio?
Si usa arc4random()
(para calcular el resto) como arc4random() % aNumber
, el resultado no se distribuye uniformemente entre el 0
y aNumber
. Hay un problema llamado sesgo de módulo .
Sesgo de módulo
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 le importa un número aleatorio en el rango 0 ..< 2
(o el intervalo [0, 3) si lo prefiere) .
Las probabilidades para números individuales son:
En otras palabras, es más probable que termines 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 abordado por SE-0202 - Unificación aleatoria en Swift 4.2
Sin arc4Random_uniform () en algunas versiones de Xcode (en 7.1 se ejecuta pero no se completa automáticamente para mí). 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)
var randomNumber = Int(arc4random_uniform(UInt32(5)))
Aquí 5 se asegurará de que el número aleatorio se genere de cero a cuatro. Puede establecer el valor en consecuencia.
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()
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 ocurrió:
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
s se utilizará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 aleatorio, pero puedes modificarlo a tu gusto :)
Swift 4.2
Swift 4.2 ha incluido una API de números aleatorios nativa y bastante completa 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
Swift 4.2, Xcode 10.1 .
Para iOS, macOS y tvOS, puede usar una fuente aleatoria de todo el sistema en el marco de Xcode GameKit
. Aquí puede encontrar la GKRandomSource
clase con su sharedRandom()
método de clase:
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 randomElement()
método 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)
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())
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]
}
}
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]
No olvide agregar el código de la solución orientada a las 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]
}
}
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]
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]
}