web-dev-qa-db-fra.com

Comment générer un nombre aléatoire dans la langue Swift d'Apple?

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?

411

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

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

495
John Pavley

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)
116
jstn

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

J'ai utilisé ce code:

var k: Int = random() % 10;
61
fockus

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 .

33
TwoStraws

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)

25
Dave DeLong

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

number, max et min sont UInt32.

Qu'en est-il de...

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.

23
Suragch

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.

19
R.S

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.

16
Ryne Wang

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.

16
Connor

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)
11
Sh_Khan

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
    }
}
9
demiculus
 let MAX : UInt32 = 9
 let MIN : UInt32 = 1

    func randomNumber()
{
    var random_number = Int(arc4random_uniform(MAX) + MIN)
    print ("random = ", random_number);
}
8
Rajesh Sharma

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.

6
Jeffrey Goldberg

Depuis Swift 4.2

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

Eh bien, qu'est-ce qui ne va pas avec la "bonne" vieille façon?

  1. Vous devez utiliser des API importées C (différentes d'une plate-forme à l'autre) .

  2. 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:

  • 0: 3/8 = 37,5%
  • 1: 3/8 = 37,5%
  • 2: 2/8 = 25%

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

6
Jakub Truhlář

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)
5
brokenrhino
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.

5
Taran Goel

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()
  1. Vous utilisez random (in :) pour générer des chiffres aléatoires à partir de plages.
  2. randomElement () renvoie la valeur nil si la plage est vide. Vous décompressez donc l'int. avec si laisser.
  3. Vous utilisez random (in :) pour générer aléatoire Double, Float ou CGFloat et random () pour renvoyer un Bool aléatoire.

Plus @ officiel

3
iSrinivasan27

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 UInt8s 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 :)

2
Zyphrax

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.

2
Suhit Patil

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)
1
ARGeo

Détails

xCode 9.1, Swift 4

Solution orientée math (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
}

Utilisation de la solution (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]

Solution orientée programmeurs (2)

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

Utilisation de la solution (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]

Échantillon complet

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

Exemple de résultat

enter image description here

1
Vasily Bodnarchuk

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]
    }
}
0
Ahmadreza Shamimi

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())
0
Durul Dalkanat