web-dev-qa-db-fra.com

Choisissez un élément aléatoire dans un tableau

Supposons que j'ai un tableau et que je veux choisir un élément au hasard.

Quel serait le moyen le plus simple de faire cela?

La manière évidente serait array[random index]. Mais peut-être y a-t-il quelque chose comme array.sample de Ruby? Ou sinon, une telle méthode pourrait-elle être créée en utilisant une extension?

171
Fela Winkelmolen

Swift 4.2 et plus

La nouvelle approche recommandée est une méthode intégrée au protocole Collection: randomElement(). Cela retourne un optionnel pour éviter le cas vide contre lequel j'avais supposé auparavant.

let array = ["Frodo", "Sam", "Wise", "Gamgee"]
print(array.randomElement()!) // Using ! knowing I have array.count > 0

Si vous ne créez pas le tableau et que le nombre de points garantis n'est pas supérieur à 0, procédez comme suit:

if let randomElement = array.randomElement() { 
    print(randomElement)
}

Swift 4.1 et ci-dessous

Juste pour répondre à votre question, vous pouvez le faire pour obtenir une sélection de tableau aléatoire:

let array = ["Frodo", "sam", "wise", "gamgee"]
let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
print(array[randomIndex])

Les castings sont laids, mais je crois qu’ils sont nécessaires à moins que quelqu'un d’autre ne le fasse autrement.

297
Lucas Derraugh

En reprenant ce que Lucas a dit, vous pouvez créer une extension de la classe Array comme ceci:

extension Array {
    func randomItem() -> Element? {
        if isEmpty { return nil }
        let index = Int(arc4random_uniform(UInt32(self.count)))
        return self[index]
    }
}

Par exemple:

let myArray = [1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16]
let myItem = myArray.randomItem() // Note: myItem is an Optional<Int>
136
Phae Deepsky

Swift 4 version:

extension Collection where Index == Int {

    /**
     Picks a random element of the collection.

     - returns: A random element of the collection.
     */
    func randomElement() -> Iterator.Element? {
        return isEmpty ? nil : self[Int(arc4random_uniform(UInt32(endIndex)))]
    }

}
45
Andrey Gordeev

Dans Swift 2.2 cela peut être généralisé de sorte que nous ayons:

UInt.random
UInt8.random
UInt16.random
UInt32.random
UInt64.random
UIntMax.random

// closed intervals:

(-3...3).random
(Int.min...Int.max).random

// and collections, which return optionals since they can be empty:

(1..<4).sample
[1,2,3].sample
"abc".characters.sample
["a": 1, "b": 2, "c": 3].sample

Tout d'abord, implémenter la propriété statique random pour UnsignedIntegerTypes:

import Darwin

func sizeof <T> (_: () -> T) -> Int { // sizeof return type without calling
    return sizeof(T.self)
}

let ARC4Foot: Int = sizeof(arc4random)

extension UnsignedIntegerType {
    static var max: Self { // sadly `max` is not required by the protocol
        return ~0
    }
    static var random: Self {
        let foot = sizeof(Self)
        guard foot > ARC4Foot else {
            return numericCast(arc4random() & numericCast(max))
        }
        var r = UIntMax(arc4random())
        for i in 1..<(foot / ARC4Foot) {
            r |= UIntMax(arc4random()) << UIntMax(8 * ARC4Foot * i)
        }
        return numericCast(r)
    }
}

Ensuite, pour ClosedIntervals avec UnsignedIntegerType bornes:

extension ClosedInterval where Bound : UnsignedIntegerType {
    var random: Bound {
        guard start > 0 || end < Bound.max else { return Bound.random }
        return start + (Bound.random % (end - start + 1))
    }
}

Puis (un peu plus impliqué), pour ClosedIntervals avec SignedIntegerType bornes (en utilisant les méthodes d'assistance décrites plus loin):

extension ClosedInterval where Bound : SignedIntegerType {
    var random: Bound {
        let foot = sizeof(Bound)
        let distance = start.unsignedDistanceTo(end)
        guard foot > 4 else { // optimisation: use UInt32.random if sufficient
            let off: UInt32
            if distance < numericCast(UInt32.max) {
                off = UInt32.random % numericCast(distance + 1)
            } else {
                off = UInt32.random
            }
            return numericCast(start.toIntMax() + numericCast(off))
        }
        guard distance < UIntMax.max else {
            return numericCast(IntMax(bitPattern: UIntMax.random))
        }
        let off = UIntMax.random % (distance + 1)
        let x = (off + start.unsignedDistanceFromMin).plusMinIntMax
        return numericCast(x)
    }
}

... où unsignedDistanceTo, unsignedDistanceFromMin et plusMinIntMax méthodes auxiliaires peuvent être implémentées comme suit:

extension SignedIntegerType {
    func unsignedDistanceTo(other: Self) -> UIntMax {
        let _self = self.toIntMax()
        let other = other.toIntMax()
        let (start, end) = _self < other ? (_self, other) : (other, _self)
        if start == IntMax.min && end == IntMax.max {
            return UIntMax.max
        }
        if start < 0 && end >= 0 {
            let s = start == IntMax.min ? UIntMax(Int.max) + 1 : UIntMax(-start)
            return s + UIntMax(end)
        }
        return UIntMax(end - start)
    }
    var unsignedDistanceFromMin: UIntMax {
        return IntMax.min.unsignedDistanceTo(self.toIntMax())
    }
}

extension UIntMax {
    var plusMinIntMax: IntMax {
        if self > UIntMax(IntMax.max) { return IntMax(self - UIntMax(IntMax.max) - 1) }
        else { return IntMax.min + IntMax(self) }
    }
}

Enfin, pour toutes les collections où Index.Distance == Int:

extension CollectionType where Index.Distance == Int {
    var sample: Generator.Element? {
        if isEmpty { return nil }
        let end = UInt(count) - 1
        let add = (0...end).random
        let idx = startIndex.advancedBy(Int(add))
        return self[idx]
    }
}

... qui peut être optimisé un peu pour l'entier Ranges:

extension Range where Element : SignedIntegerType {
    var sample: Element? {
        guard startIndex < endIndex else { return nil }
        let i: ClosedInterval = startIndex...endIndex.predecessor()
        return i.random
    }
}

extension Range where Element : UnsignedIntegerType {
    var sample: Element? {
        guard startIndex < endIndex else { return nil }
        let i: ClosedInterval = startIndex...endIndex.predecessor()
        return i.random
    }
}
21
milos

Vous pouvez également utiliser la fonction random () intégrée de Swift pour l'extension:

extension Array {
    func sample() -> Element {
        let randomIndex = Int(Rand()) % count
        return self[randomIndex]
    }
}

let array = [1, 2, 3, 4]

array.sample() // 2
array.sample() // 2
array.sample() // 3
array.sample() // 3

array.sample() // 1
array.sample() // 1
array.sample() // 3
array.sample() // 1
18
NatashaTheRobot

Une autre suggestion Swift 3

private extension Array {
    var randomElement: Element {
        let index = Int(arc4random_uniform(UInt32(count)))
        return self[index]
    }
}
9
slammer

Suivant les autres répondent mais avec Swift 2 support.

Swift 1.x

extension Array {
    func sample() -> T {
        let index = Int(arc4random_uniform(UInt32(self.count)))
        return self[index]
    }
}

Swift 2.x

extension Array {
    func sample() -> Element {
        let index = Int(arc4random_uniform(UInt32(self.count)))
        return self[index]
    }
}

Par exemple.:

let arr = [2, 3, 5, 7, 9, 11, 13, 17, 19, 23, 29, 31]
let randomSample = arr.sample()
4
Aidan Gomez

Si vous voulez pouvoir extraire plus d'un élément aléatoire de votre tableau avec sans doublon , GameplayKit vous a couvert:

import GameplayKit
let array = ["one", "two", "three", "four"]

let shuffled = GKMersenneTwisterRandomSource.sharedRandom().arrayByShufflingObjects(in: array)

let firstRandom = shuffled[0]
let secondRandom = shuffled[1]

Vous avez plusieurs choix pour le hasard, voir GKRandomSource :

La classe GKARC4RandomSource utilise un algorithme similaire à celui utilisé dans la famille arc4random de fonctions C. (Cependant, les instances de cette classe sont indépendantes des appels aux fonctions arc4random.)

La classe GKLinearCongruentialRandomSource utilise un algorithme plus rapide, mais moins aléatoire, que la classe GKARC4RandomSource. (Plus précisément, les bits bas des nombres générés se répètent plus souvent que les bits hauts.) Utilisez cette source lorsque les performances sont plus importantes que l'imprévisibilité robuste.

La classe GKMersenneTwisterRandomSource utilise un algorithme plus lent, mais plus aléatoire, que la classe GKARC4RandomSource. Utilisez cette source quand il est important que votre utilisation de nombres aléatoires ne montre pas de répétition et que les performances sont moins préoccupantes.

2
bcattle

Swift 3

importer GameKit

func getRandomMessage() -> String {

    let messages = ["one", "two", "three"]

    let randomNumber = GKRandomSource.sharedRandom().nextInt(upperBound: messages.count)

    return messages[randomNumber].description

}
2
Dazzle

Une autre implémentation fonctionnelle avec vérification du tableau vide.

func randomArrayItem<T>(array: [T]) -> T? {
  if array.isEmpty { return nil }
  let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
  return array[randomIndex]
}

randomArrayItem([1,2,3])
2
Evgenii

Swift 3 - simple facile à utiliser.

  1. Créer un tableau

    var arrayOfColors = [UIColor.red, UIColor.yellow, UIColor.orange, UIColor.green]
    
  2. Créer des couleurs aléatoires

    let randomColor = arc4random() % UInt32(arrayOfColors.count)
    
  3. Définissez cette couleur sur votre objet

    your item = arrayOfColors[Int(randomColor)]
    

Voici un exemple tiré d'un projet SpriteKit mettant à jour un SKLabelNode avec un String aléatoire:

    let array = ["one","two","three","four","five"]

    let randomNumber = arc4random() % UInt32(array.count)

    let labelNode = SKLabelNode(text: array[Int(randomNumber)])
2
Timmy Sorensen

Voici une extension sur les tableaux avec un contrôle de tableau vide pour plus de sécurité:

extension Array {
    func sample() -> Element? {
        if self.isEmpty { return nil }
        let randomInt = Int(arc4random_uniform(UInt32(self.count)))
        return self[randomInt]
    }
}

Vous pouvez l'utiliser aussi simplement que cela :

let digits = Array(0...9)
digits.sample() // => 6

Si vous préférez un Framework qui présente également des fonctionnalités plus pratiques, cliquez sur checkout HandySwift. Vous pouvez l'ajouter à votre projet via Carthage puis l'utiliser exactement comme dans l'exemple ci-dessus:

import HandySwift    

let digits = Array(0...9)
digits.sample() // => 8

En outre, il inclut également une option permettant d’obtenir plusieurs éléments aléatoires à la fois :

digits.sample(size: 3) // => [8, 0, 7]
2
Dschee

Il y a une méthode intégrée sur Collection maintenant:

let foods = ["????", "????", "????", "????"]
let myDinner = foods.randomElement()

Si vous voulez extraire jusqu'à n éléments aléatoires d'une collection, vous pouvez ajouter une extension comme celle-ci:

extension Collection {
    func randomElements(_ count: Int) -> [Element] {
        var shuffledIterator = shuffled().makeIterator()
        return (0..<count).compactMap { _ in shuffledIterator.next() }
    }
}

Et si vous voulez qu'ils soient uniques, vous pouvez utiliser un Set, mais les éléments de la collection doivent être conformes au protocole Hashable:

extension Collection where Element: Hashable {
    func randomUniqueElements(_ count: Int) -> [Element] {
        var shuffledIterator = Set(shuffled()).makeIterator()
        return (0..<count).compactMap { _ in shuffledIterator.next() }
    }
}
1
Gigisommo

Je trouve que l'utilisation de GKRandomSource.sharedRandom () de GameKit fonctionne le mieux pour moi.

import GameKit

let array = ["random1", "random2", "random3"]

func getRandomIndex() -> Int {
    let randomNumber = GKRandomSource.sharedRandom().nextIntWithUpperBound(array.count)
    return randomNumber

ou vous pouvez retourner l'objet à l'index aléatoire sélectionné. Assurez-vous que la fonction renvoie d'abord une chaîne, puis renvoyez l'index du tableau.

    return array[randomNumber]

Court et au point.

1
djames04

dernier code Swift essayez-le

 let imagesArray = ["image1.png","image2.png","image3.png","image4.png"]

        var randomNum: UInt32 = 0
        randomNum = arc4random_uniform(UInt32(imagesArray.count))
        wheelBackgroundImageView.image = UIImage(named: imagesArray[Int(randomNum)])
0