Sei que o livro Swift forneceu uma implementação de um gerador de números aleatórios. A melhor prática é copiar e colar essa implementação no próprio programa? Ou existe uma biblioteca que faz isso que podemos usar agora?
Sei que o livro Swift forneceu uma implementação de um gerador de números aleatórios. A melhor prática é copiar e colar essa implementação no próprio programa? Ou existe uma biblioteca que faz isso que podemos usar agora?
Respostas:
Swift 4.2+
O Swift 4.2 fornecido com o Xcode 10 apresenta novas funções aleatórias fáceis de usar para muitos tipos de dados. Você pode chamar o random()
método em tipos numéricos.
let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()
Use arc4random_uniform(n)
para um número inteiro aleatório entre 0 e n-1.
let diceRoll = Int(arc4random_uniform(6) + 1)
Transmitir o resultado para Int para que você não precise digitar explicitamente seus vars como UInt32
(o que parece não-Swifty).
0
. No seu código, diceRoll
poderia ser 0
. Basta dizer ...
Int(arc4random_uniform(6)+1)
.
arc3random_uniform(n)
a UInt32(n)
se você estiver usando um valor que ainda não é desse tipo.
Edit: Atualizado para Swift 3.0
arc4random
funciona bem no Swift, mas as funções básicas são limitadas a tipos inteiros de 32 bits ( Int
é de 64 bits no iPhone 5S e em Macs modernos). Aqui está uma função genérica para um número aleatório de um tipo expressável por um literal inteiro:
public func arc4random<T: ExpressibleByIntegerLiteral>(_ type: T.Type) -> T {
var r: T = 0
arc4random_buf(&r, MemoryLayout<T>.size)
return r
}
Podemos usar essa nova função genérica para estender UInt64
, adicionando argumentos de fronteira e atenuando o viés do módulo. (Isso é retirado diretamente do 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
}
}
Com isso, podemos estender Int64
os mesmos argumentos, lidando com o estouro:
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 a família ...
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
}
}
}
Depois de tudo isso, podemos finalmente fazer algo assim:
let diceRoll = UInt64.random(lower: 1, upper: 7)
var r = arc4random(UInt64)
. Por favor, informe o que você quis dizer aqui?
arc4random
(definida no primeiro bloco de código) com o argumento UInt64
que é a Type
.
arc4random_buf
. O objetivo dessas extensões é fazer exatamente o que arc4random_uniform
faz (atenuar a polarização do módulo), exceto para os tipos de 64 bits.
Editar para o Swift 4.2
A partir do Swift 4.2, em vez de usar a função C importada arc4random_uniform (), agora você pode usar as funções nativas do Swift.
// Generates integers starting with 0 up to, and including, 10
Int.random(in: 0 ... 10)
Você também pode usar random(in:)
para obter valores aleatórios para outros valores primitivos; como Int, Double, Float e até Bool.
Versões Swift <4.2
Este método irá gerar um Int
valor aleatório entre o mínimo e o máximo indicados
func randomInt(min: Int, max: Int) -> Int {
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
Eu usei este código:
var k: Int = random() % 10;
random is unavailable in Swift: Use arc4random instead.
No iOS 9, você pode usar as novas classes GameplayKit para gerar números aleatórios de várias maneiras.
Você tem quatro tipos de fonte para escolher: uma fonte aleatória geral (sem nome, até o sistema escolher o que faz), congruencial linear, ARC4 e Mersenne Twister. Isso pode gerar entradas, flutuadores e bools aleatórios.
No nível mais simples, você pode gerar um número aleatório a partir da fonte aleatória interna do sistema, como esta:
GKRandomSource.sharedRandom().nextInt()
Isso gera um número entre -2.147.483.648 e 2.147.483.647. Se você deseja um número entre 0 e um limite superior (exclusivo), use este:
GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
O GameplayKit possui alguns construtores de conveniência incorporados para trabalhar com dados. Por exemplo, você pode rolar um dado de seis lados assim:
let d6 = GKRandomDistribution.d6()
d6.nextInt()
Além disso, você pode moldar a distribuição aleatória usando coisas como GKShuffledDistribution. Isso leva um pouco mais de explicação, mas se você estiver interessado, pode ler meu tutorial sobre os números aleatórios do GameplayKit .
import GameplayKit
. Swift 3 alterou a sintaxe paraGKRandomSource.sharedRandom().nextInt(upperBound: 6)
Você pode fazer da mesma maneira que faria em C:
let randomNumber = arc4random()
randomNumber
é inferido como sendo do tipo UInt32
(um número inteiro não assinado de 32 bits)
rand
, arc4random
, drand48
e amigos estão todos no Darwin
módulo. Ele já foi importado para você se você estiver criando um aplicativo Cocoa, UIKit ou Foundation, mas será necessário import Darwin
em playgrounds.
arc4random_uniform()
Uso:
arc4random_uniform(someNumber: UInt32) -> UInt32
Isso fornece números inteiros aleatórios no intervalo 0
de someNumber - 1
.
O valor máximo para UInt32
é 4.294.967.295 (ou seja, 2^32 - 1
).
Exemplos:
Coin flip
let flip = arc4random_uniform(2) // 0 or 1
Rolar os dados
let roll = arc4random_uniform(6) + 1 // 1...6
Dia aleatório em outubro
let day = arc4random_uniform(31) + 1 // 1...31
Ano aleatório nos anos 90
let year = 1990 + arc4random_uniform(10)
Forma geral:
let number = min + arc4random_uniform(max - min + 1)
onde number
, max
e min
estão UInt32
.
arc4random ()
Você também pode obter um número aleatório usando arc4random()
, que produz UInt32
entre 0 e 2 ^ 32-1. Assim, para obter um número aleatório entre 0
e x-1
, você pode dividi-lo x
e pegar o restante. Ou, em outras palavras, use o Operador restante (%) :
let number = arc4random() % 5 // 0...4
No entanto, isso produz um leve viés de módulo (veja também aqui e aqui ), e é por isso que arc4random_uniform()
é recomendado.
Convertendo de e para Int
Normalmente, seria bom fazer algo assim para converter entre Int
e UInt32
:
let number: Int = 10
let random = Int(arc4random_uniform(UInt32(number)))
O problema, porém, é que Int
tem um intervalo de -2,147,483,648...2,147,483,647
sistemas de 32 bits e um de -9,223,372,036,854,775,808...9,223,372,036,854,775,807
sistemas de 64 bits. Compare isso com o UInt32
intervalo de 0...4,294,967,295
. O U
de UInt32
significa não assinado .
Considere os seguintes erros:
UInt32(-1) // negative numbers cause integer overflow error
UInt32(4294967296) // numbers greater than 4,294,967,295 cause integer overflow error
Portanto, você só precisa ter certeza de que seus parâmetros de entrada estão dentro do UInt32
intervalo e também não precisa de uma saída que esteja fora desse intervalo.
Consegui usar apenas rand()
para obter um CInt aleatório. Você pode torná-lo um Int usando algo como isto:
let myVar: Int = Int(rand())
Você pode usar sua função aleatória C favorita e apenas converter em valor para Int, se necessário.
random()
, que retorna um Int
e não UInt32
- e, como o @SomeGuy mencionado, simplesmente chame srandom(arc4random())
uma vez em qualquer lugar antes de usá-lo para garantir que ele tenha uma semente aleatória diferente para cada execução do seu programa.
A resposta do @ jstn é boa, mas um pouco detalhada. O Swift é conhecido como uma linguagem orientada a protocolo, para que possamos obter o mesmo resultado sem precisar implementar o código padrão para todas as classes da família inteira, adicionando uma implementação padrão para a extensão do protocolo.
public extension ExpressibleByIntegerLiteral {
public static func arc4random() -> Self {
var r: Self = 0
arc4random_buf(&r, MemoryLayout<Self>.size)
return r
}
}
Agora podemos fazer:
let i = Int.arc4random()
let j = UInt32.arc4random()
e todas as outras classes inteiras estão ok.
No Swift 4.2, você pode gerar números aleatórios chamando o random()
método para qualquer tipo numérico desejado, fornecendo o intervalo com o qual deseja trabalhar. Por exemplo, isso gera um número aleatório no intervalo de 1 a 9, inclusive nos dois lados
let randInt = Int.random(in: 1..<10)
Também com outros tipos
let randFloat = Float.random(in: 1..<20)
let randDouble = Double.random(in: 1...30)
let randCGFloat = CGFloat.random(in: 1...40)
Aqui está uma biblioteca que faz o trabalho bem 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);
}
Gostaria de acrescentar às respostas existentes que o exemplo de gerador de números aleatórios no livro Swift é um gerador de congruência linear (LCG), é severamente limitado e não deve ser, exceto pelos exemplos triviais, em que a qualidade da aleatoriedade não importa mesmo. E um LCG nunca deve ser usado para fins criptográficos .
arc4random()
é muito melhor e pode ser usado para a maioria dos propósitos, mas novamente não deve ser usado para fins de criptografia.
Se você deseja que algo que seja garantido criptograficamente seja seguro, use SecCopyRandomBytes()
. Observe que, se você criar um gerador de números aleatórios em alguma coisa, outra pessoa poderá acabar (utilizando incorretamente) para fins criptográficos (como senha, chave ou geração de sal), então você deve considerar o uso de SecCopyRandomBytes()
qualquer maneira, mesmo que sua necessidade não seja ' isso exige bastante.
Há um novo conjunto de APIs:
let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomDouble = Double.random(in: 1 ... 10)
Todos os tipos numéricos agora têm o random(in:)
método necessário range
.
Retorna um número uniformemente distribuído nesse intervalo.
TL; DR
Você precisa usar APIs C importadas (elas são diferentes entre plataformas) .
E além disso...
E se eu lhe dissesse que o aleatório não é tão aleatório?
Se você usar arc4random()
(para calcular o restante) como arc4random() % aNumber
, o resultado não será distribuído uniformemente entre 0
e aNumber
. Há um problema chamado viés do módulo .
Viés do módulo
Normalmente, a função gera um número aleatório entre 0
e MAX (depende do tipo etc.) . Para dar um exemplo rápido e fácil, digamos que o número máximo seja 7
e você se preocupa com um número aleatório no intervalo 0 ..< 2
(ou no intervalo [0, 3), se preferir) .
As probabilidades para números individuais são:
Em outras palavras, é mais provável que você termine com 0 ou 1 que 2 . Obviamente, tenha em mente que isso é extremamente simplificado e o número MAX é muito maior, tornando-o mais "justo".
Esse problema foi solucionado pelo SE-0202 - Unificação aleatória no Swift 4.2
Sem arc4Random_uniform () em algumas versões do Xcode (na 7.1, ele é executado, mas não é preenchido automaticamente). Você pode fazer isso.
Para gerar um número aleatório de 0 a 5. Primeiro
import GameplayKit
Então
let diceRoll = GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
var randomNumber = Int(arc4random_uniform(UInt32(5)))
Aqui 5 garantirá que o número aleatório seja gerado de zero a quatro. Você pode definir o valor de acordo.
Swift 4.2
Adeus para importar a 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()
O código a seguir produzirá um número aleatório seguro entre 0 e 255:
extension UInt8 {
public static var random: UInt8 {
var number: UInt8 = 0
_ = SecRandomCopyBytes(kSecRandomDefault, 1, &number)
return number
}
}
Você chama assim:
print(UInt8.random)
Para números maiores, fica mais complicado.
Este é o melhor que eu poderia ter:
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) }
}
}
Esses métodos usam um número aleatório extra para determinar quantos UInt8
s serão usados para criar o número aleatório. A última linha converte o [UInt8]
para UInt16
ou UInt32
.
Não sei se os dois últimos ainda contam como verdadeiramente aleatórios, mas você pode ajustá-lo ao seu gosto :)
Swift 4.2
O Swift 4.2 incluiu uma API de número aleatório nativa e bastante completa na biblioteca padrão. ( Proposta Swift Evolution SE-0202 )
let intBetween0to9 = Int.random(in: 0...9)
let doubleBetween0to1 = Double.random(in: 0...1)
Todos os tipos de números têm o aleatório estático (em :), que pega o intervalo e retorna o número aleatório no determinado intervalo
Swift 4.2, Xcode 10.1 .
Para iOS, macOS e tvOS, você pode usar fonte aleatória em todo o sistema na estrutura do Xcode GameKit
. Aqui você pode encontrar GKRandomSource
classe com seu sharedRandom()
método de classe:
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()
Ou apenas use um randomElement()
método que retorne um elemento aleatório da coleção:
let number: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let randomNumber = number.randomElement()!
print(randomNumber)
Você pode usar GeneratorOf
assim:
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())
Eu uso esse código para gerar um número aleatório:
//
// 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]
Não se esqueça de adicionar o código da solução orientada a matemática (1) aqui
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]
Não se esqueça de adicionar os códigos de solução (1) e solução (2) aqui
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]
}