Comment mélanger un tableau dans Swift?

305

Comment randomiser ou mélanger les éléments d'un tableau dans Swift? Par exemple, si mon tableau se compose de 52 cartes à jouer, je veux mélanger le tableau afin de mélanger le jeu.

mpatzer
la source
2
ce n'est spécifique à aucune langue. Il suffit d'appliquer n'importe quel algorithme de mélange ...
Gabriele Petronella
8
@Mithrandir Ce n'est pas vrai. En Ruby, on irait pour array.shuffle. Il n'est pas nécessaire d'implémenter votre propre version. Je suppose que OP recherchait quelque chose de similaire.
Linus Oleander
1
Attention cependant, n'utilisez pas n'importe quel algorithme de mélange pour mélanger un jeu de cartes.
njzk2

Réponses:

627

Cette réponse détaille comment mélanger avec un algorithme rapide et uniforme (Fisher-Yates) dans Swift 4.2+ et comment ajouter la même fonctionnalité dans les différentes versions précédentes de Swift. Le nom et le comportement de chaque version de Swift correspondent aux méthodes de tri mutantes et non mutantes de cette version.

Swift 4.2+

shuffleet shuffledsont natifs à partir de Swift 4.2. Exemple d'utilisation:

let x = [1, 2, 3].shuffled()
// x == [2, 3, 1]

let fiveStrings = stride(from: 0, through: 100, by: 5).map(String.init).shuffled()
// fiveStrings == ["20", "45", "70", "30", ...]

var numbers = [1, 2, 3, 4]
numbers.shuffle()
// numbers == [3, 2, 1, 4]

Swift 4.0 et 4.1

Ces extensions ajoutent une shuffle()méthode à toute collection mutable (tableaux et tampons mutables non sécurisés) et une shuffled()méthode à n'importe quelle séquence:

extension MutableCollection {
    /// Shuffles the contents of this collection.
    mutating func shuffle() {
        let c = count
        guard c > 1 else { return }

        for (firstUnshuffled, unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
            // Change `Int` in the next line to `IndexDistance` in < Swift 4.1
            let d: Int = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
            let i = index(firstUnshuffled, offsetBy: d)
            swapAt(firstUnshuffled, i)
        }
    }
}

extension Sequence {
    /// Returns an array with the contents of this sequence, shuffled.
    func shuffled() -> [Element] {
        var result = Array(self)
        result.shuffle()
        return result
    }
}

Même utilisation que dans les exemples Swift 4.2 ci-dessus.


Swift 3

Ces extensions ajoutent une shuffle()méthode à toute collection mutable et une shuffled()méthode à n'importe quelle séquence:

extension MutableCollection where Indices.Iterator.Element == Index {
    /// Shuffles the contents of this collection.
    mutating func shuffle() {
        let c = count
        guard c > 1 else { return }

        for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
            // Change `Int` in the next line to `IndexDistance` in < Swift 3.2
            let d: Int = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
            guard d != 0 else { continue }
            let i = index(firstUnshuffled, offsetBy: d)
            self.swapAt(firstUnshuffled, i)
        }
    }
}

extension Sequence {
    /// Returns an array with the contents of this sequence, shuffled.
    func shuffled() -> [Iterator.Element] {
        var result = Array(self)
        result.shuffle()
        return result
    }
}

Même utilisation que dans les exemples Swift 4.2 ci-dessus.


Swift 2

(langage obsolète: vous ne pouvez pas utiliser Swift 2.x pour publier sur iTunes Connect à partir de juillet 2018)

extension MutableCollectionType where Index == Int {
    /// Shuffle the elements of `self` in-place.
    mutating func shuffleInPlace() {
        // empty and single-element collections don't shuffle
        if count < 2 { return }

        for i in startIndex ..< endIndex - 1 {
            let j = Int(arc4random_uniform(UInt32(count - i))) + i
            guard i != j else { continue }
            swap(&self[i], &self[j])
        }
    }
}

extension CollectionType {
    /// Return a copy of `self` with its elements shuffled.
    func shuffle() -> [Generator.Element] {
        var list = Array(self)
        list.shuffleInPlace()
        return list
    }
}

Usage:

[1, 2, 3].shuffle()
// [2, 3, 1]

let fiveStrings = 0.stride(through: 100, by: 5).map(String.init).shuffle()
// ["20", "45", "70", "30", ...]

var numbers = [1, 2, 3, 4]
numbers.shuffleInPlace()
// [3, 2, 1, 4]

Swift 1.2

(langage obsolète: vous ne pouvez pas utiliser Swift 1.x pour publier sur iTunes Connect à partir de juillet 2018)

shuffle comme méthode de tableau mutant

Cette extension vous permettra de mélanger une Arrayinstance mutable en place:

extension Array {
    mutating func shuffle() {
        if count < 2 { return }
        for i in 0..<(count - 1) {
            let j = Int(arc4random_uniform(UInt32(count - i))) + i
            swap(&self[i], &self[j])
        }
    }
}
var numbers = [1, 2, 3, 4, 5, 6, 7, 8]
numbers.shuffle()                     // e.g., numbers == [6, 1, 8, 3, 2, 4, 7, 5]

shuffled comme méthode de tableau non mutant

Cette extension vous permettra de récupérer une copie mélangée d'une Arrayinstance:

extension Array {
    func shuffled() -> [T] {
        if count < 2 { return self }
        var list = self
        for i in 0..<(list.count - 1) {
            let j = Int(arc4random_uniform(UInt32(list.count - i))) + i
            swap(&list[i], &list[j])
        }
        return list
    }
}
let numbers = [1, 2, 3, 4, 5, 6, 7, 8]
let mixedup = numbers.shuffled()     // e.g., mixedup == [6, 1, 8, 3, 2, 4, 7, 5]
Nate Cook
la source
1
Dans le cas où vous souhaitez que la version de la fonction dans Swift 1.2, elle a besoin d'un peu de mise à jour car elle a countElementsdisparu, et son remplacement, countrenvoie maintenant un T.Index.Distancedonc la contrainte doit être activée C.Index.Distance == Int. Cette version devrait fonctionner: gist.github.com/airspeedswift/03d07a9dc86fabdc370f
Airspeed Velocity
2
Ce sont les résultats réels - Fisher-Yates devrait renvoyer une permutation aléatoire non biaisée de la source, donc il n'y a aucune exigence qu'un élément particulier se déplace. Il y a une garantie qu'aucun élément ne se déplace plus d'une fois, mais parfois le "déplacement" est vers le même index. Le cas le plus simple est de réfléchir [1, 2].shuffled(): cela devrait-il revenir à [2, 1]chaque fois?
Nate Cook
1
J'ai ajouté if count > 0en haut de la fonction de tableau en mutation, pour éviter de recevoir une "erreur fatale: impossible de former une plage avec end <start" lors du passage d'un tableau vide.
Carl Smith
3
@Jan: Oui, ajoutez guard i != j else { continue }avant l'échange. J'ai déposé un radar, mais le nouveau comportement est intentionnel.
Nate Cook
3
En fait, shuffleInPlacepeut se bloquer si les indices de collection ne commencent pas à zéro, par exemple pour une tranche de tableau. for i in 0..<count - 1 devrait être for i in startIndex ..< endIndex - 1(et puis la conversion à Swift 3 devient presque triviale).
Martin R
131

Edit: Comme indiqué dans d'autres réponses, Swift 4.2 enfin ajoute la génération de nombres aléatoires à la bibliothèque standard, avec un brassage de tableaux.

Cependant, GKRandom/ GKRandomDistributionsuite dans GameplayKit peut toujours être utile avec le nouveau RandomNumberGeneratorprotocole - si vous ajoutez des extensions aux RNG GameplayKit pour se conformer au nouveau protocole de bibliothèque standard, vous pouvez facilement obtenir:

  • des RNGs envoyables (qui peuvent reproduire une séquence "aléatoire" si nécessaire pour les tests)
  • Des RNG qui sacrifient la robustesse pour la vitesse
  • RNG qui produisent des distributions non uniformes

... et toujours utiliser les nouvelles API aléatoires "natives" de Swift.

Le reste de cette réponse concerne ces RNG et / ou leur utilisation dans les anciens compilateurs Swift.


Il y a déjà de bonnes réponses ici, ainsi que de bonnes illustrations de la raison pour laquelle l'écriture de votre propre mélange peut être source d'erreurs si vous ne faites pas attention.

Dans iOS 9, macOS 10.11 et tvOS 9 (ou version ultérieure), vous n'avez pas à écrire le vôtre. Il y a une implémentation efficace et correcte de Fisher-Yates dans GameplayKit (qui, malgré son nom, n'est pas seulement pour les jeux).

Si vous voulez juste un remaniement unique:

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

Si vous voulez pouvoir répliquer un shuffle ou une série de shuffles, choisissez et amorcez une source aléatoire spécifique; par exemple

let lcg = GKLinearCongruentialRandomSource(seed: mySeedValue)
let shuffled = lcg.arrayByShufflingObjects(in: array)

Dans iOS 10 / macOS 10.12 / tvOS 10, il existe également une syntaxe pratique pour mélanger via une extension activée NSArray. Bien sûr, c'est un peu lourd lorsque vous utilisez un Swift Array(et il perd son type d'élément en revenant à Swift):

let shuffled1 = (array as NSArray).shuffled(using: random) // -> [Any]
let shuffled2 = (array as NSArray).shuffled() // use default random source

Mais il est assez facile de faire un wrapper Swift préservant le type pour cela:

extension Array {
    func shuffled(using source: GKRandomSource) -> [Element] {
        return (self as NSArray).shuffled(using: source) as! [Element]
    }
    func shuffled() -> [Element] {
        return (self as NSArray).shuffled() as! [Element]
    }
}
let shuffled3 = array.shuffled(using: random)
let shuffled4 = array.shuffled()
rickster
la source
6
Me fait me demander quels autres utilitaires utiles peuvent être trouvés dans GameplayKit que je n'ai jamais explorés!
Richard Venable
6
Recherche de graphiques, recherche d'arbres, systèmes de règles ... beaucoup de choses utiles à la fois dans la conception de jeux et ailleurs
rickster du
5
Dans Swift 3 / iOS 10, cela a été changé en:let shuffled = lcg.arrayByShufflingObjects(in: array)
Evan Pon
30

Dans Swift 2.0 , GameplayKit peut venir à la rescousse! (pris en charge par iOS9 ou version ultérieure)

import GameplayKit

func shuffle() {
    array = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(array)
}
bluenowhere
la source
5
importer GameplayKit juste pour obtenir un tableau mélangé ne semble pas être une excellente idée
Lope
3
Pourquoi? Cela fait partie du système, n'ajoute rien au binaire.
Abizern
3
Vous pouvez également étendre l'importation à simplementimport GameplayKit.GKRandomSource
JRG-Developer
26

Voici peut-être un peu plus court:

sorted(a) {_, _ in arc4random() % 2 == 0}
Jean Le Moignan
la source
1
@moby La sortfonction a besoin d'une fermeture pour ordonner les éléments. Cette fermeture prend deux paramètres (elem1, elem2) et doit retourner true si la première valeur doit apparaître avant la deuxième valeur, false sinon. Si nous retournons un booléen aléatoire à la place ... alors nous mélangeons le tout :)
Jean Le Moignan
2
Un mathématicien ici pour confirmer ou infirmer?
Jean Le Moignan
9
Comme l'a souligné pjs en réponse à une autre réponse très similaire, cela ne générera pas une distribution uniforme des résultats. Utilisez Fisher-Yates Shuffle comme indiqué dans la réponse de Nate Cook.
Rob
1
C'est une astuce astucieuse, mais qui est épouvantable en termes de qualité du shuffle. D'une part, cette fermeture devrait utiliser arc4random_uniform(), car elle est actuellement soumise au biais modulo. Deuxièmement, la sortie dépend très fortement de l'algorithme de tri (que nous ne connaissons pas sans regarder la source).
Alexander - Reinstate Monica
1
Poursuivant avec cette approche plus simple, cela semble fonctionner très bien: collection.sorted { _,_ in arc4random_uniform(1) == 0 }
Markiv
7

En prenant l' algorithme de Nate, je voulais voir à quoi cela ressemblerait avec Swift 2 et les extensions de protocole.

C'est ce que j'ai trouvé.

extension MutableCollectionType where Self.Index == Int {
    mutating func shuffleInPlace() {
        let c = self.count
        for i in 0..<(c - 1) {
            let j = Int(arc4random_uniform(UInt32(c - i))) + i
            swap(&self[i], &self[j])
        }
    }
}

extension MutableCollectionType where Self.Index == Int {
    func shuffle() -> Self {
        var r = self
        let c = self.count
        for i in 0..<(c - 1) {
            let j = Int(arc4random_uniform(UInt32(c - i))) + i
            swap(&r[i], &r[j])
        }
        return r
    }
}

Maintenant, tout MutableCollectionType peut utiliser ces méthodes étant donné qu'il utilise IntcommeIndex

Chris Wagner
la source
6

Dans mon cas, j'ai eu quelques problèmes de permutation d'objets dans Array. Puis je me suis gratté la tête et suis allé réinventer la roue.

// swift 3.0 ready
extension Array {

    func shuffled() -> [Element] {
        var results = [Element]()
        var indexes = (0 ..< count).map { $0 }
        while indexes.count > 0 {
            let indexOfIndexes = Int(arc4random_uniform(UInt32(indexes.count)))
            let index = indexes[indexOfIndexes]
            results.append(self[index])
            indexes.remove(at: indexOfIndexes)
        }
        return results
    }

}
Kaz Yoshikawa
la source
5

Il s'agit d'une version de l'implémentation par Nate du shuffle Fisher-Yates pour Swift 4 (Xcode 9).

extension MutableCollection {
    /// Shuffle the elements of `self` in-place.
    mutating func shuffle() {
        for i in indices.dropLast() {
            let diff = distance(from: i, to: endIndex)
            let j = index(i, offsetBy: numericCast(arc4random_uniform(numericCast(diff))))
            swapAt(i, j)
        }
    }
}

extension Collection {
    /// Return a copy of `self` with its elements shuffled
    func shuffled() -> [Element] {
        var list = Array(self)
        list.shuffle()
        return list
    }
}

Les changements sont les suivants:

  • La contrainte Indices.Iterator.Element == Indexfait maintenant partie du Collectionprotocole et n'a plus besoin d'être imposée à l'extension.
  • L'échange d'éléments doit se faire en faisant appel swapAt()à la collection, comparer SE-0173 AddMutableCollection.swapAt(_:_:) .
  • Elementest un alias pour Iterator.Element.
Martin R
la source
3

Voici ce que j'utilise:

func newShuffledArray(array:NSArray) -> NSArray {
    var mutableArray = array.mutableCopy() as! NSMutableArray
    var count = mutableArray.count
    if count>1 {
        for var i=count-1;i>0;--i{
            mutableArray.exchangeObjectAtIndex(i, withObjectAtIndex: Int(arc4random_uniform(UInt32(i+1))))
        }
    }
    return mutableArray as NSArray
}
iliketopgun
la source
3

Swift 4 Mélangez les éléments d'un tableau dans une boucle for où i est le rapport de mélange

var cards = [Int]() //Some Array
let i = 4 // is the mixing ratio
func shuffleCards() {
    for _ in 0 ..< cards.count * i {
        let card = cards.remove(at: Int(arc4random_uniform(UInt32(cards.count))))
        cards.insert(card, at: Int(arc4random_uniform(UInt32(cards.count))))
    }
}

Ou avec l'extension Int

func shuffleCards() {
    for _ in 0 ..< cards.count * i {
        let card = cards.remove(at: cards.count.arc4random)
        cards.insert(card, at: cards.count.arc4random)
    }
}
extension Int {
    var arc4random: Int {
        if self > 0 {
            print("Arc for random positiv self \(Int(arc4random_uniform(UInt32(self))))")
        return Int(arc4random_uniform(UInt32(self)))
        } else if self < 0 {
            print("Arc for random negotiv self \(-Int(arc4random_uniform(UInt32(abs(self)))))")
            return -Int(arc4random_uniform(UInt32(abs(self))))
        } else {
            print("Arc for random equal 0")
            return 0
        }
    }
}
Sergei
la source
2

Solution Swift 3, suite à la réponse de @Nate Cook: (fonctionne si l'index commence par 0, voir les commentaires ci-dessous)

extension Collection {
    /// Return a copy of `self` with its elements shuffled
    func shuffle() -> [Generator.Element] {
        var list = Array(self)
        list.shuffleInPlace()
        return list
    } }

extension MutableCollection where Index == Int {
    /// Shuffle the elements of `self` in-place.
    mutating func shuffleInPlace() {
        // empty and single-element collections don't shuffle
        if count < 2 { return }
        let countInt = count as! Int

    for i in 0..<countInt - 1 {
        let j = Int(arc4random_uniform(UInt32(countInt - i))) + i
            guard i != j else { continue }
            swap(&self[i], &self[j])
        }
    }
}
Anson Yao
la source
1
Cela peut se bloquer si les indices de collection commencent à 0, par exemple pour une tranche de tableau. Essayez d'exécuter var a = [1, 2, 3, 4, 5, 6][3..<6]; a.shuffleInPlace()plusieurs fois. - Voir stackoverflow.com/a/37843901/1187415 pour une solution correcte.
Martin R
2

Voilà comment c'est fait de la manière la plus simple. import Gamplaykità votre VC et utilisez le code ci-dessous. Testé dans Xcode 8.

 import GameplayKit

 let array: NSArray = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]

 override func viewDidLoad() {
    super.viewDidLoad()

    print(array.shuffled())  
}

Si vous souhaitez obtenir une chaîne mélangée à partir d'un tableau, vous pouvez utiliser le code ci-dessous.

func suffleString() {

    let ShuffleArray = array.shuffled()

    suffleString.text = ShuffleArray.first as? String

    print(suffleString.text!)

}
Joe
la source
2

Avec Swift 3, si vous souhaitez mélanger un tableau en place ou obtenir un nouveau tableau mélangé à partir d'un tableau, cela AnyIteratorpeut vous aider. L'idée est de créer un tableau d'index à partir de votre tableau, de mélanger ces indices avec une AnyIteratorinstance et une swap(_:_:)fonction et de mapper chaque élément de cette AnyIteratorinstance avec l'élément correspondant du tableau.


Le code Playground suivant montre comment cela fonctionne:

import Darwin // required for arc4random_uniform

let array = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]
var indexArray = Array(array.indices)
var index = indexArray.endIndex

let indexIterator: AnyIterator<Int> = AnyIterator {
    guard let nextIndex = indexArray.index(index, offsetBy: -1, limitedBy: indexArray.startIndex)
        else { return nil }

    index = nextIndex
    let randomIndex = Int(arc4random_uniform(UInt32(index)))
    if randomIndex != index {
        swap(&indexArray[randomIndex], &indexArray[index])
    }

    return indexArray[index]
}

let newArray = indexIterator.map { array[$0] }
print(newArray) // may print: ["Jock", "Ellie", "Sue Ellen", "JR", "Pamela", "Bobby"]

Vous pouvez refactoriser le code précédent et créer une shuffled()fonction à l'intérieur d'une Arrayextension afin d'obtenir un nouveau tableau mélangé à partir d'un tableau:

import Darwin // required for arc4random_uniform

extension Array {

    func shuffled() -> Array<Element> {
        var indexArray = Array<Int>(indices)        
        var index = indexArray.endIndex

        let indexIterator = AnyIterator<Int> {
            guard let nextIndex = indexArray.index(index, offsetBy: -1, limitedBy: indexArray.startIndex)
                else { return nil }

            index = nextIndex                
            let randomIndex = Int(arc4random_uniform(UInt32(index)))
            if randomIndex != index {
                swap(&indexArray[randomIndex], &indexArray[index])
            }

            return indexArray[index]
        }

        return indexIterator.map { self[$0] }
    }

}

Usage:

let array = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]
let newArray = array.shuffled()
print(newArray) // may print: ["Bobby", "Pamela", "Jock", "Ellie", "JR", "Sue Ellen"]
let emptyArray = [String]()
let newEmptyArray = emptyArray.shuffled()
print(newEmptyArray) // prints: []

Comme alternative au code précédent, vous pouvez créer une shuffle()fonction à l'intérieur d'une Arrayextension afin de mélanger un tableau en place:

import Darwin // required for arc4random_uniform

extension Array {

    mutating func shuffle() {
        var indexArray = Array<Int>(indices)
        var index = indexArray.endIndex

        let indexIterator = AnyIterator<Int> {
            guard let nextIndex = indexArray.index(index, offsetBy: -1, limitedBy: indexArray.startIndex)
                else { return nil }

            index = nextIndex                
            let randomIndex = Int(arc4random_uniform(UInt32(index)))
            if randomIndex != index {
                swap(&indexArray[randomIndex], &indexArray[index])
            }

            return indexArray[index]
        }

        self = indexIterator.map { self[$0] }
    }

}

Usage:

var mutatingArray = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]
mutatingArray.shuffle()
print(mutatingArray) // may print ["Sue Ellen", "Pamela", "Jock", "Ellie", "Bobby", "JR"]
Imanou Petit
la source
1

Vous pouvez également utiliser la swapfonction générique et implémenter les Fisher-Yates mentionnés:

for idx in 0..<arr.count {
  let rnd = Int(arc4random_uniform(UInt32(idx)))
  if rnd != idx {
    swap(&arr[idx], &arr[rnd])
  }
}

ou moins verbeux:

for idx in 0..<steps.count {
  swap(&steps[idx], &steps[Int(arc4random_uniform(UInt32(idx)))])
}
Daniel Bauke
la source
2
Cela souffre, à tout le moins, d'une grave erreur par une erreur décrite ici, par laquelle une valeur est toujours permutée de sa position d'origine. Ceci est résolu avec let rnd = Int(arc4random_uniform(UInt32(idx + 1))). De plus, dans FY, vous itérez généralement de arr.count - 1bas en haut 1(ou si vous itérez de 0en arr.count - 1, vous choisissez un index comme Nate montre dans la réponse acceptée). Voir la section Algorithme moderne de la discussion de Fisher-Yates.
Rob
1

travaux!!. organismes est le tableau à mélanger.

extension Array
{
    /** Randomizes the order of an array's elements. */
    mutating func shuffle()
    {
        for _ in 0..<10
        {
            sort { (_,_) in arc4random() < arc4random() }
        }
    }
}

var organisms = [
    "ant",  "bacteria", "cougar",
    "dog",  "elephant", "firefly",
    "goat", "hedgehog", "iguana"]

print("Original: \(organisms)")

organisms.shuffle()

print("Shuffled: \(organisms)")
Vimal
la source
0

Voici comment mélanger un tableau avec une graine dans Swift 3.0.

extension MutableCollection where Indices.Iterator.Element == Index {
    mutating func shuffle() {
        let c = count
        guard c > 1 else { return }


        for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
            srand48(seedNumber)
            let number:Int = numericCast(unshuffledCount)
            let r = floor(drand48() * Double(number))

            let d: IndexDistance = numericCast(Int(r))
            guard d != 0 else { continue }
            let i = index(firstUnshuffled, offsetBy: d)
            swap(&self[firstUnshuffled], &self[i])
        }
    }
}
Tayo119
la source
0
let shuffl = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: arrayObject)
Rohit Sisodia
la source
0

Voici ce que j'utilise:

import GameplayKit

extension Collection {
    func shuffled() -> [Iterator.Element] {
        let shuffledArray = (self as? NSArray)?.shuffled()
        let outputArray = shuffledArray as? [Iterator.Element]
        return outputArray ?? []
    }
    mutating func shuffle() {
        if let selfShuffled = self.shuffled() as? Self {
            self = selfShuffled
        }
    }
}

// Usage example:

var numbers = [1,2,3,4,5]
numbers.shuffle()

print(numbers) // output example: [2, 3, 5, 4, 1]

print([10, "hi", 9.0].shuffled()) // output example: [hi, 10, 9]
Daniel Illescas
la source
0

Exemple simple:

extension Array {
    mutating func shuffled() {
        for _ in self {
            // generate random indexes that will be swapped
            var (a, b) = (Int(arc4random_uniform(UInt32(self.count - 1))), Int(arc4random_uniform(UInt32(self.count - 1))))
            if a == b { // if the same indexes are generated swap the first and last
                a = 0
                b = self.count - 1
            }
            swap(&self[a], &self[b])
        }
    }
}

var array = [1,2,3,4,5,6,7,8,9,10]
array.shuffled()
print(array) // [9, 8, 3, 5, 7, 6, 4, 2, 1, 10]
Policier
la source
0

Extension de baie de travail (mutante et non mutante)

Swift 4.1 / Xcode 9

La première réponse est obsolète, j'ai donc décidé de créer ma propre extension pour mélanger un tableau dans la dernière version de Swift, Swift 4.1 (Xcode 9):

extension Array {

// Non-mutating shuffle
    var shuffled : Array {
        let totalCount : Int = self.count
        var shuffledArray : Array = []
        var count : Int = totalCount
        var tempArray : Array = self
        for _ in 0..<totalCount {
            let randomIndex : Int = Int(arc4random_uniform(UInt32(count)))
            let randomElement : Element = tempArray.remove(at: randomIndex)
            shuffledArray.append(randomElement)
            count -= 1
        }
        return shuffledArray
    }

// Mutating shuffle
    mutating func shuffle() {
        let totalCount : Int = self.count
        var shuffledArray : Array = []
        var count : Int = totalCount
        var tempArray : Array = self
        for _ in 0..<totalCount {
            let randomIndex : Int = Int(arc4random_uniform(UInt32(count)))
            let randomElement : Element = tempArray.remove(at: randomIndex)
            shuffledArray.append(randomElement)
            count -= 1
        }
        self = shuffledArray
    }
}

Appel Shuffle sans mutation [Array] -> [Array]:

let array = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]

print(array.shuffled)

Cela s'imprime arraydans un ordre aléatoire.


Appel mutant Shuffle [Array] = [Array]:

var array = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]

array.shuffle() 
// The array has now been mutated and contains all of its initial 
// values, but in a randomized shuffled order

print(array) 

Cela s'imprime arraydans son ordre actuel, qui a déjà été mélangé au hasard.


J'espère que cela fonctionne pour tout le monde, si vous avez des questions, des suggestions ou des commentaires, n'hésitez pas à demander!

Noah Wilder
la source
0

Dans SWIFT 4

func createShuffledSequenceOfNumbers(max:UInt)->[UInt] {

    var array:[UInt]! = []
    var myArray:[UInt]! = []
    for i in 1...max {
        myArray.append(i)
    }
    for i in 1...max {
        array.append(i)
    }
    var tempArray:[Int]! = []
    for index in 0...(myArray.count - 1) {

        var isNotFinded:Bool = true
        while(isNotFinded){

            let randomNumber = arc4random_uniform(UInt32(myArray.count))
            let randomIndex = Int(randomNumber)

            if(!tempArray.contains(randomIndex)){
                tempArray.append(randomIndex)

                array[randomIndex] = myArray[index]
                isNotFinded = false
            }
        }
    }

    return array
}
Ali khezri
la source
0

Si vous souhaitez utiliser la fonction de boucle Swift For simple, utilisez ceci ->

var arrayItems = ["A1", "B2", "C3", "D4", "E5", "F6", "G7", "H8", "X9", "Y10", "Z11"]
var shuffledArray = [String]()

for i in 0..<arrayItems.count
{
    let randomObject = Int(arc4random_uniform(UInt32(items.count)))

    shuffledArray.append(items[randomObject])

    items.remove(at: randomObject)
}

print(shuffledArray)

Swift Array suffle en utilisant l'extension ->

extension Array {
    // Order Randomize
    mutating func shuffle() {
        for _ in 0..<count {
            sort { (_,_) in arc4random() < arc4random() }
        }
    }
}
Rahul Singha Roy
la source
0

Depuis swift 4.2, il existe deux fonctions pratiques:

// shuffles the array in place
myArray.shuffle()

et

// generates a new array with shuffled elements of the old array
let newArray = myArray.shuffled()
lynx rapide
la source
-2

Voici un code qui s'exécute dans la cour de récréation. Vous n'aurez pas besoin d'importer Darwin dans un projet Xcode réel.

import darwin

var a = [1,2,3,4,5,6,7]

func shuffle<ItemType>(item1: ItemType, item2: ItemType) -> Bool {
    return drand48() > 0.5
}

sort(a, shuffle)

println(a)
Dan Hixon
la source
7
Cela donne une distribution non uniforme des résultats. Ce sera également O (n log n), où un remaniement de Fisher-Yates donnerait des résultats uniformément répartis en temps O (n).
pjs
drand48()Donne également les mêmes nombres pseudo aléatoires à chaque fois, sauf si vous définissez une graine avec commesrand48(Int(arc4random()))
Kametrixom
-3

Il s'arrête à «swap (& self [i], & self [j])» lorsque je mets à niveau la version xCode vers la version 7.4 bêta.
erreur fatale: l'échange d'un emplacement avec lui-même n'est pas pris en charge

J'ai trouvé la raison pour laquelle i = j (la fonction de swap va exploser)

J'ajoute donc une condition comme ci-dessous

if (i != j){
    swap(&list[i], &list[j])
}

YA! C'est bon pour moi.

米米米
la source
Cela semble être un commentaire sur la réponse de Chris , pas une réponse à la question d'origine.
Mogsdad