Scegli un elemento casuale da un array


189

Supponiamo di avere un array e di voler scegliere un elemento a caso.

Quale sarebbe il modo più semplice per farlo?

Il modo ovvio sarebbe array[random index]. Ma forse c'è qualcosa come Ruby array.sample? O se non fosse possibile creare un metodo del genere utilizzando un'estensione?


1
Hai già provato metodi diversi?
ford prefect,

Vorrei provare array[random number from 0 to length-1], ma non riesco a trovare rapidamente un int casuale, lo chiederei su overflow dello stack se non fossi bloccato :) Non volevo inquinare la domanda con mezze soluzioni quando forse c'è qualcosa come rubyarray.sample
Fela Winkelmolen,

1
Usi arc4random () come faresti in Obj-C
Arbitur il

Nessuna spiegazione del perché la tua domanda non ha ricevuto lo stesso feedback della controparte JQuery. Ma in generale, dovresti seguire queste linee guida quando pubblichi una domanda. Come fare una buona domanda? . Fai sembrare che hai fatto un piccolo sforzo per trovare una soluzione prima di chiedere aiuto a qualcun altro. Quando google "scelgo il numero casuale rapido", la prima pagina è piena di risposte che suggeriscono arc4random_uniform. Inoltre, RTFD ... "leggi la documentazione di finzione". È sorprendente quante domande si possano rispondere in questo modo.
Austin,

Grazie per il tuo gentile feedback. Sì, credo che avrei dovuto rispondere io stesso alla domanda, ma mi è sembrato abbastanza facile che fosse bello dare a qualcun altro i punti reputazione quasi gratuiti. E l'ho scritto quando nemmeno i rapidi documenti ufficiali di Apple erano pubblici, in quel momento non c'erano risultati Google. Ma la domanda era una volta a -12, quindi sono abbastanza fiducioso che alla fine sarà positivo :)
Fela Winkelmolen,

Risposte:


321

Swift 4.2 e versioni successive

Il nuovo approccio consigliato è un built-in metodo sul protocollo Collection: randomElement(). Restituisce un optional per evitare il caso vuoto che avevo assunto in precedenza.

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

Se non crei l'array e non hai il conteggio garantito> 0, dovresti fare qualcosa del tipo:

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

Swift 4.1 e versioni precedenti

Solo per rispondere alla tua domanda, puoi farlo per ottenere una selezione casuale dell'array:

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

I casting sono brutti, ma credo che siano necessari a meno che qualcun altro abbia un altro modo.


4
Perché Swift non offre un generatore di numeri casuali che restituisce un Int? Questa seconda riga sembra molto dettagliata solo per restituire Int. Scelto casualmente C'è qualche vantaggio computazionale / sintattico nel restituire un UInt32 invece di un Int? Inoltre, perché Swift non offre un'alternativa Int a questa funzione o non consente a un utente di specificare quale tipo di numero intero desidera restituire?
Austin,

Per aggiungere una nota, questo metodo di generazione di numeri casuali potrebbe impedire la "distorsione del modulo". Fare riferimento man arc4randome stackoverflow.com/questions/10984974/...
Kent Liau

1
@AustinA, Swift 4.2 ha una funzione generatrice di numeri casuali nativa che è implementata su tutti i tipi di dati scalari che potresti desiderare: Int, Double, Float, UInt32, ecc. E ti consente di fornire intervalli target per i valori. Molto maneggevole. Puoi usare l'array [Int.random (0 .. <array.count)] `in Swift 4.2
Duncan C

Vorrei che Swift 4.2 implementasse una removeRandomElement()funzione in aggiunta a randomElement(). Sarebbe modellato removeFirst(), ma rimuove un oggetto in un indice casuale.
Duncan C,

@DuncanC Dovresti evitare 0..<array.count(per diversi motivi, il principale è che non funziona per gli slice, ed è soggetto a errori). Puoi farlo let randomIndex = array.indices.randomElement(), seguito da let randomElement = array.remove(at: randomIndex). Potresti persino incorporarlo let randomElement = array.remove(at: array.indices.randomElement()).
Alexander - Ripristina Monica il

137

Riflettendo su ciò che Lucas ha detto, potresti creare un'estensione della classe Array in questo modo:

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

Per esempio:

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

2
In swift 2 Tè stato rinominato in Element.
GDanger,

25
Si noti che un array vuoto causerà un arresto anomalo qui
Berik,

1
@Berik Beh, potresti restituire un elemento opzionale e quindi fare sempre un guardcontrollo per vedere se l'array è vuoto e quindi tornare nil.
Harish,

1
Concordato. Le matrici si arrestano in modo anomalo in modo che possano essere performanti. La chiamata in arc4randomrende qualsiasi aumento delle prestazioni completamente insignificante. Ho aggiornato la risposta.
Berik,

45

Versione Swift 4 :

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

}

Questo può andare in crash con un indice fuori limite nelle raccolte dovestartIndex != 0
dan

21

In Swift 2.2 questo può essere generalizzato in modo da avere:

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

Innanzitutto, implementando la randomproprietà statica per 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)
    }
}

Quindi, per ClosedIntervals con UnsignedIntegerTypelimiti:

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

Quindi (un po 'più coinvolto), per ClosedIntervals con SignedIntegerTypelimiti (usando i metodi di aiuto descritti più avanti):

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

... dove unsignedDistanceTo, unsignedDistanceFromMine plusMinIntMaxhelper metodi possono essere implementati come segue:

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

Infine, per tutte le collezioni in cui 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]
    }
}

... che può essere leggermente ottimizzato per numeri interi Range:

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

18

Puoi usare anche la funzione random () incorporata di Swift per l'estensione:

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

In realtà random () proviene dal bridging della libreria Standard C, puoi vederlo e gli amici nel Terminale, "man random". Ma sono contento che tu abbia sottolineato la disponibilità!
David H,

1
questo produce ogni volta la stessa sequenza casuale
iTSangar

1
@iTSangar hai ragione! rand () è quello corretto da usare. Aggiornamento della mia risposta.
NatashaTheRobot

6
Questo è anche suscettibile di distorsioni del modulo.
Aidan Gomez,

@mattt ha scritto un bell'articolo sulla generazione di numeri casuali . TL; DR qualsiasi della famiglia arc4random è una scelta migliore.
elitalon,

9

Un altro suggerimento di Swift 3

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

4

Seguono gli altri che rispondono ma con il supporto di Swift 2.

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

Per esempio:

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

2

Un'implementazione funzionale alternativa con controllo per array vuoto.

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

Ecco un'estensione su array con un controllo di array vuoto per maggiore sicurezza:

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

Puoi usarlo così semplice :

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

Se preferisci un Framework che ha anche alcune funzioni più utili, dai un'occhiata a HandySwift . Puoi aggiungerlo al tuo progetto tramite Cartagine e usarlo esattamente come nell'esempio sopra:

import HandySwift    

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

Inoltre include anche un'opzione per ottenere più elementi casuali contemporaneamente :

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

2

Swift 3

importare GameKit

func getRandomMessage() -> String {

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

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

    return messages[randomNumber].description

}

2

Swift 3 - semplice facile da usare.

  1. Crea array

    var arrayOfColors = [UIColor.red, UIColor.yellow, UIColor.orange, UIColor.green]
  2. Crea colore casuale

    let randomColor = arc4random() % UInt32(arrayOfColors.count)
  3. Imposta quel colore sul tuo oggetto

    your item = arrayOfColors[Int(randomColor)]

Ecco un esempio da un SpriteKitprogetto che aggiorna un SKLabelNodecon un casuale String:

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

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

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

2

Se vuoi essere in grado di ottenere più di un elemento casuale dal tuo array senza duplicati , GameplayKit ti copre:

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

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

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

Hai un paio di scelte per casualità, vedi GKRandomSource :

La GKARC4RandomSourceclasse utilizza un algoritmo simile a quello impiegato nella famiglia di funzioni C arc4random. (Tuttavia, le istanze di questa classe sono indipendenti dalle chiamate alle funzioni arc4random.)

La GKLinearCongruentialRandomSourceclasse utilizza un algoritmo più veloce, ma meno casuale, rispetto alla classe GKARC4RandomSource. (In particolare, i bit bassi dei numeri generati si ripetono più spesso dei bit alti.) Utilizzare questa sorgente quando le prestazioni sono più importanti della imprevedibilità.

La GKMersenneTwisterRandomSourceclasse utilizza un algoritmo più lento, ma più casuale, rispetto alla classe GKARC4RandomSource. Utilizzare questa fonte quando è importante che l'uso di numeri casuali non mostri schemi ripetitivi e che le prestazioni siano meno preoccupanti.


1

Trovo che usare GKRandomSource.sharedRandom () di GameKit funzioni meglio per me.

import GameKit

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

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

oppure potresti restituire l'oggetto all'indice casuale selezionato. Assicurarsi che la funzione restituisca prima una stringa, quindi restituisce l'indice dell'array.

    return array[randomNumber]

Breve e dritto al punto.


1

Esiste un metodo integrato Collectionora:

let foods = ["🍕", "🍔", "🍣", "🍝"]
let myDinner = foods.randomElement()

Se vuoi estrarre fino a nelementi casuali da una raccolta puoi aggiungere un'estensione come questa:

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

E se vuoi che siano univoci puoi usare a Set, ma gli elementi della raccolta devono essere conformi al Hashableprotocollo:

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

0

L'ultimo codice swift3 prova che funziona bene

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

-2

Ho trovato un modo molto diverso di farlo usando le nuove funzionalità introdotte in Swift 4.2.

// 👇🏼 - 1 
public func shufflePrintArray(ArrayOfStrings: [String]) -> String {
// - 2 
       let strings = ArrayOfStrings
//- 3
       var stringans =  strings.shuffled()
// - 4
        var countS = Int.random(in: 0..<strings.count)
// - 5
        return stringans[countS] 
}

  1. abbiamo dichiarato una funzione con parametri che assumono una matrice di stringhe e restituiscono una stringa.

  2. Quindi prendiamo ArrayOfStrings in una variabile.

  3. Quindi chiamiamo la funzione mescolata e la memorizziamo in una variabile. (Supportato solo in 4.2)
  4. Quindi dichiariamo una variabile che salva un valore mischiato del conteggio totale della stringa.
  5. Infine restituiamo la stringa mescolata al valore dell'indice di countS.

Fondamentalmente sta mescolando l'array di stringhe e quindi ha anche una scelta casuale del numero del numero totale di conteggi e quindi restituisce l'indice casuale dell'array mischiato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.