Je réalise que le livre Swift fournit une implémentation d'un générateur de nombres aléatoires. La meilleure pratique consiste-t-elle à copier et coller cette mise en œuvre dans son propre programme? Ou y a-t-il une bibliothèque qui fait cela que nous pouvons utiliser maintenant?
Swift 4.2 +
Swift 4.2 fourni avec Xcode 10 introduit de nouvelles fonctions aléatoires faciles à utiliser pour de nombreux types de données. Vous pouvez appeler la méthode random()
sur des types numériques.
let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()
Utilisez arc4random_uniform(n)
pour un entier aléatoire compris entre 0 et n-1.
let diceRoll = Int(arc4random_uniform(6) + 1)
Transformez le résultat en Int pour ne pas avoir à taper explicitement vos vars sous la forme UInt32
(ce qui ne semble pas être Swifty).
Modifier: Mise à jour pour le Swift 3.
arc4random
fonctionne bien dans Swift, mais les fonctions de base sont limitées aux types entiers 32 bits (Int
est 64 bits sur iPhone 5S et les Mac modernes). Voici une fonction générique pour un nombre aléatoire d'un type exprimable par un littéral entier:
public func arc4random<T: ExpressibleByIntegerLiteral>(_ type: T.Type) -> T {
var r: T = 0
arc4random_buf(&r, MemoryLayout<T>.size)
return r
}
Nous pouvons utiliser cette nouvelle fonction générique pour étendre UInt64
, en ajoutant des arguments de frontière et en atténuant le biais modulo. (Ceci est levé directement 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
}
}
Avec cela, nous pouvons étendre Int64
pour les mêmes arguments, traitant du débordement:
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
}
}
}
Pour compléter la famille ...
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
}
}
}
Après tout cela, nous pouvons enfin faire quelque chose comme ça:
let diceRoll = UInt64.random(lower: 1, upper: 7)
Editer pour Swift 4.2
À partir de Swift 4.2, au lieu d’utiliser la fonction C importée arc4random_uniform (), vous pouvez désormais utiliser les fonctions natives de Swift.
// Generates integers starting with 0 up to, and including, 10
Int.random(in: 0 ... 10)
Vous pouvez utiliser random(in:)
pour obtenir des valeurs aléatoires pour d'autres valeurs primitives également; tels que Int, Double, Float et même Bool.
versions Swift <4.2
Cette méthode générera une valeur aléatoire Int
entre le minimum et le maximum donnés
func randomInt(min: Int, max: Int) -> Int {
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
J'ai utilisé ce code:
var k: Int = random() % 10;
Depuis iOS 9, vous pouvez utiliser les nouvelles classes GameplayKit pour générer des nombres aléatoires de différentes manières.
Vous avez le choix entre quatre types de source: une source aléatoire générale (sans nom, au système de choisir ce qu’elle fait), linéaire congruentielle, ARC4 et Mersenne Twister. Ceux-ci peuvent générer des ints, des floats et des bools aléatoires.
Au niveau le plus simple, vous pouvez générer un nombre aléatoire à partir de la source aléatoire intégrée du système, comme suit:
GKRandomSource.sharedRandom().nextInt()
Cela génère un nombre compris entre -2 147 483 648 et 2 147 483 647. Si vous voulez un nombre compris entre 0 et une limite supérieure (exclusive), vous utiliserez ceci:
GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
GameplayKit a des constructeurs de confort intégrés pour fonctionner avec des dés. Par exemple, vous pouvez lancer un dé à six faces comme ceci:
let d6 = GKRandomDistribution.d6()
d6.nextInt()
De plus, vous pouvez modeler la distribution aléatoire en utilisant des éléments tels que GKShuffledDistribution. Cela prend un peu plus d'explications, mais si cela vous intéresse, vous pouvez lisez mon tutoriel sur les nombres aléatoires de GameplayKit .
Vous pouvez le faire de la même manière qu'en C:
let randomNumber = arc4random()
randomNumber
est supposé être de type UInt32
(un entier non signé de 32 bits)
arc4random_uniform()
Utilisation:
arc4random_uniform(someNumber: UInt32) -> UInt32
Cela vous donne des entiers aléatoires dans la plage 0
à someNumber - 1
.
La valeur maximale de UInt32
est de 4 294 967 295 (c'est-à-dire 2^32 - 1
).
Exemples:
Coin flip
let flip = arc4random_uniform(2) // 0 or 1
Lancer de dés
let roll = arc4random_uniform(6) + 1 // 1...6
Jour aléatoire d'octobre
let day = arc4random_uniform(31) + 1 // 1...31
Année aléatoire dans les années 1990
let year = 1990 + arc4random_uniform(10)
Forme générale:
let number = min + arc4random_uniform(max - min + 1)
où number
, max
et min
sont UInt32
.
arc4random ()
Vous pouvez également obtenir un nombre aléatoire en utilisant arc4random()
, ce qui produit un UInt32
compris entre 0 et 2 ^ 32-1. Ainsi, pour obtenir un nombre aléatoire entre 0
et x-1
, vous pouvez le diviser par x
et prendre le reste. Ou en d'autres termes, utilisez le Opérateur de reste (%) :
let number = arc4random() % 5 // 0...4
Cependant, ceci produit le léger biais modulo (voir aussi ici et ici ), c'est pourquoi arc4random_uniform()
est recommandé.
Conversion vers et depuis Int
Normalement, il serait bien de faire quelque chose comme ceci pour pouvoir effectuer des conversions entre Int
et UInt32
:
let number: Int = 10
let random = Int(arc4random_uniform(UInt32(number)))
Le problème, cependant, est que Int
a une plage de -2,147,483,648...2,147,483,647
sur des systèmes 32 bits et une plage de -9,223,372,036,854,775,808...9,223,372,036,854,775,807
sur des systèmes 64 bits. Comparez ceci à la gamme UInt32
de 0...4,294,967,295
. Le U
de UInt32
signifie unsigned .
Considérez les erreurs suivantes:
UInt32(-1) // negative numbers cause integer overflow error
UInt32(4294967296) // numbers greater than 4,294,967,295 cause integer overflow error
Vous devez donc simplement vous assurer que vos paramètres d'entrée se situent dans la plage UInt32
et que vous n'avez pas besoin non plus d'une sortie située en dehors de cette plage.
Exemple pour un nombre aléatoire compris entre 10 (0-9);
import UIKit
let randomNumber = Int(arc4random_uniform(10))
Code très facile - simple et court.
La réponse de @ jstn est bonne, mais un peu verbeuse. Swift est un langage orienté protocole, ce qui permet d'obtenir le même résultat sans avoir à implémenter de code passe-partout pour chaque classe de la famille des nombres entiers, en ajoutant une implémentation par défaut pour l'extension de protocole.
public extension ExpressibleByIntegerLiteral {
public static func arc4random() -> Self {
var r: Self = 0
arc4random_buf(&r, MemoryLayout<Self>.size)
return r
}
}
Maintenant nous pouvons faire:
let i = Int.arc4random()
let j = UInt32.arc4random()
et toutes les autres classes entières sont ok.
J'ai pu simplement utiliser Rand()
pour obtenir un CInt aléatoire. Vous pouvez en faire un Int en utilisant quelque chose comme ceci:
let myVar: Int = Int(Rand())
Vous pouvez utiliser votre fonction aléatoire C favorite, et simplement convertir en valeur Int si nécessaire.
Dans Swift 4.2, vous pouvez générer des nombres aléatoires en appelant la méthode random()
sur le type numérique de votre choix, en indiquant la plage avec laquelle vous souhaitez travailler. Par exemple, cela génère un nombre aléatoire compris entre 1 et 9, inclus des deux côtés.
let randInt = Int.random(in: 1..<10)
Aussi avec d'autres types
let randFloat = Float.random(in: 1..<20)
let randDouble = Double.random(in: 1...30)
let randCGFloat = CGFloat.random(in: 1...40)
Voici une bibliothèque qui fait le travail bien 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);
}
Je voudrais ajouter aux réponses existantes que l’exemple de générateur de nombres aléatoires dans le livre Swift est un générateur de congruences linéaires (LCG). Il est extrêmement limité et ne devrait pas être mis à part les exemples triviaux indispensables. , où la qualité du hasard n’a aucune importance. Et n LCG ne devrait jamais être utilisé à des fins cryptographiques.
arc4random()
est bien meilleur et peut être utilisé dans la plupart des cas, mais encore une fois ne devrait pas être utilisé à des fins cryptographiques.
Si vous souhaitez que quelque chose soit garanti de manière cryptographique, utilisez SecCopyRandomBytes()
. Notez que si vous construisez un générateur de nombre aléatoire en quelque chose, quelqu'un d'autre pourrait finir par l'utiliser (à mauvais escient) à des fins cryptographiques (telles que la génération d'un mot de passe, d'une clé ou d'un sel), vous devriez alors envisager d'utiliser SecCopyRandomBytes()
de toute façon, même si votre besoin ne l'exige pas vraiment.
Il y a un nouvel ensemble d'API:
let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomDouble = Double.random(in: 1 ... 10)
Tous les types numériques ont maintenant la méthode random(in:)
qui prend range
.
Il retourne un nombre uniformément distribué dans cette plage.
TL; DR
Vous devez utiliser des API importées C (différentes d'une plate-forme à l'autre) .
Et de plus...
Et si je vous disais que le hasard n'est pas si aléatoire?
Si vous utilisez arc4random()
(pour calculer le reste) comme arc4random() % aNumber
, le résultat n'est pas uniformément réparti entre le 0
et aNumber
. Il y a un problème appelé le biais Modulo .
Biais modulo
Normalement, la fonction génère un nombre aléatoire entre 0
et MAX (dépend du type, etc.) . . Pour créer un exemple simple et rapide, supposons que le nombre maximal est 7
et que vous tenez compte d'un nombre aléatoire compris dans la plage 0 ..< 2
(ou l'intervalle [0, 3) si vous préférez cela) .
Les probabilités pour des nombres individuels sont:
En d’autres termes, vous êtes plus susceptible de vous retrouver avec 0 ou 1 que 2 . Bien sûr, sachez que ceci est extrêmement simplifié et que le nombre MAX est beaucoup plus élevé, ce qui le rend plus "juste".
Ce problème est résolu par SE-0202 - Unification aléatoire dans Swift 4.2
Sans arc4Random_uniform () dans certaines versions de Xcode (il fonctionne en version 7.1 mais ne se complète pas automatiquement). Vous pouvez le faire à la place.
Pour générer un nombre aléatoire de 0 à 5. Première
import GameplayKit
Ensuite
let diceRoll = GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
var randomNumber = Int(arc4random_uniform(UInt32(**5**)))
Ici 5 s'assurera que le nombre aléatoire est généré bien que zéro à cinq. Vous pouvez définir la valeur en conséquence.
Swift 4.2
Adieu, importez 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()
Le code suivant produira un nombre aléatoire sécurisé compris entre 0 et 255:
extension UInt8 {
public static var random: UInt8 {
var number: UInt8 = 0
_ = SecRandomCopyBytes(kSecRandomDefault, 1, &number)
return number
}
}
Vous l'appelez comme ça:
print(UInt8.random)
Pour les grands nombres, cela devient plus compliqué.
C’est ce que je pourrais faire de mieux:
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) }
}
}
Ces méthodes utilisent un nombre aléatoire supplémentaire pour déterminer combien de UInt8
s seront utilisés pour créer le nombre aléatoire. La dernière ligne convertit le [UInt8]
en UInt16
ou UInt32
.
Je ne sais pas si les deux derniers comptent toujours comme vraiment aléatoires, mais vous pouvez le modifier à votre guise :)
Swift 4.2
Swift 4.2 a inclus une API de nombres aléatoires native et relativement complète dans la bibliothèque standard. ( proposition Swift Evolution SE-0202 )
let intBetween0to9 = Int.random(in: 0...9)
let doubleBetween0to1 = Double.random(in: 0...1)
Tous les types de nombres ont la valeur statique random (in:) qui prend la plage et renvoie le nombre aléatoire dans la plage donnée.
Swift 4.2, Xcode 10.1.
Pour iOS, macOS et tvOS, vous pouvez utiliser source aléatoire à l'échelle du système dans le framework Xcode GameKit
. Ici vous pouvez trouver la classe GKRandomSource
avec sa méthode 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()
Ou utilisez simplement une méthode randomElement()
qui renvoie un élément aléatoire de la collection:
let number: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let randomNumber = number.randomElement()!
print(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'oubliez pas d'ajouter ici le code de la solution orientée mathématique (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]
N'oubliez pas d'ajouter les codes solution (1) et solution (2) ici
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]
}
J'utilise ce code pour générer un nombre aléatoire:
//
// 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]
}
}
Vous pouvez utiliser GeneratorOf
comme ceci:
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())