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.
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:
extensionMutableCollection{/// Shuffles the contents of this collection.
mutatingfunc shuffle(){let c = count
guard c >1else{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)}}}extensionSequence{/// 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:
extensionMutableCollectionwhereIndices.Iterator.Element==Index{/// Shuffles the contents of this collection.
mutatingfunc shuffle(){let c = count
guard c >1else{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 !=0else{continue}let i = index(firstUnshuffled, offsetBy: d)self.swapAt(firstUnshuffled, i)}}}extensionSequence{/// 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)
extensionMutableCollectionTypewhereIndex==Int{/// Shuffle the elements of `self` in-place.
mutatingfunc 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])}}}extensionCollectionType{/// 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:
extensionArray{mutatingfunc shuffle(){if count <2{return}for i in0..<(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:
extensionArray{func shuffled()->[T]{if count <2{returnself}var list =selffor i in0..<(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]
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 asNSArray).shuffled(using: random)// -> [Any]
let shuffled2=(array asNSArray).shuffled()// use default random source
Mais il est assez facile de faire un wrapper Swift préservant le type pour cela:
@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é.
extensionMutableCollectionTypewhereSelf.Index==Int{mutatingfunc shuffleInPlace(){let c =self.count
for i in0..<(c -1){let j =Int(arc4random_uniform(UInt32(c - i)))+ i
swap(&self[i],&self[j])}}}extensionMutableCollectionTypewhereSelf.Index==Int{func shuffle()->Self{var r =selflet c =self.count
for i in0..<(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
extensionMutableCollection{/// Shuffle the elements of `self` in-place.
mutatingfunc 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)}}}extensionCollection{/// 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.
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_in0..< 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_in0..< cards.count * i {let card = cards.remove(at: cards.count.arc4random)
cards.insert(card, at: cards.count.arc4random)}}extensionInt{var arc4random:Int{ifself>0{
print("Arc for random positiv self \(Int(arc4random_uniform(UInt32(self))))")returnInt(arc4random_uniform(UInt32(self)))}elseifself<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")return0}}}
Solution Swift 3, suite à la réponse de @Nate Cook: (fonctionne si l'index commence par 0, voir les commentaires ci-dessous)
extensionCollection{/// Return a copy of `self` with its elements shuffled
func shuffle()->[Generator.Element]{var list =Array(self)
list.shuffleInPlace()return list
}}extensionMutableCollectionwhereIndex==Int{/// Shuffle the elements of `self` in-place.
mutatingfunc shuffleInPlace(){// empty and single-element collections don't shuffle
if count <2{return}let countInt = count as!Intfor i in0..<countInt -1{let j =Int(arc4random_uniform(UInt32(countInt - i)))+ i
guard i != j else{continue}
swap(&self[i],&self[j])}}}
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.
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:
importDarwin// 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{guardlet nextIndex = indexArray.index(index, offsetBy:-1, limitedBy: indexArray.startIndex)else{returnnil}
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:
importDarwin// required for arc4random_uniform
extensionArray{func shuffled()->Array<Element>{var indexArray =Array<Int>(indices)var index = indexArray.endIndex
let indexIterator =AnyIterator<Int>{guardlet nextIndex = indexArray.index(index, offsetBy:-1, limitedBy: indexArray.startIndex)else{returnnil}
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:
importDarwin// required for arc4random_uniform
extensionArray{mutatingfunc shuffle(){var indexArray =Array<Int>(indices)var index = indexArray.endIndex
let indexIterator =AnyIterator<Int>{guardlet nextIndex = indexArray.index(index, offsetBy:-1, limitedBy: indexArray.startIndex)else{returnnil}
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"]
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.
extensionArray{/** Randomizes the order of an array's elements. */mutatingfunc shuffle(){for_in0..<10{
sort {(_,_)in arc4random()< arc4random()}}}}var organisms =["ant","bacteria","cougar","dog","elephant","firefly","goat","hedgehog","iguana"]
print("Original: \(organisms)")
organisms.shuffle()
print("Shuffled: \(organisms)")
Dans Swift 4.2 , il existe désormais une méthode à la fois modifiableshuffle et immuableshuffled . Vous pouvez en savoir plus sur la génération aléatoire et les éléments de tableau ici .
extensionArray{mutatingfunc shuffled(){for_inself{// 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]
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):
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!
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)
array.shuffle
. Il n'est pas nécessaire d'implémenter votre propre version. Je suppose que OP recherchait quelque chose de similaire.Réponses:
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+
shuffle
etshuffled
sont natifs à partir de Swift 4.2. Exemple d'utilisation: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 uneshuffled()
méthode à n'importe quelle séquence: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 uneshuffled()
méthode à n'importe quelle séquence: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)
Usage:
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 mutantCette extension vous permettra de mélanger une
Array
instance mutable en place:shuffled
comme méthode de tableau non mutantCette extension vous permettra de récupérer une copie mélangée d'une
Array
instance:la source
countElements
disparu, et son remplacement,count
renvoie maintenant unT.Index.Distance
donc la contrainte doit être activéeC.Index.Distance == Int
. Cette version devrait fonctionner: gist.github.com/airspeedswift/03d07a9dc86fabdc370f[1, 2].shuffled()
: cela devrait-il revenir à[2, 1]
chaque fois?if count > 0
en 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.guard i != j else { continue }
avant l'échange. J'ai déposé un radar, mais le nouveau comportement est intentionnel.shuffleInPlace
peut 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 êtrefor i in startIndex ..< endIndex - 1
(et puis la conversion à Swift 3 devient presque triviale).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
/GKRandomDistribution
suite dans GameplayKit peut toujours être utile avec le nouveauRandomNumberGenerator
protocole - si vous ajoutez des extensions aux RNG GameplayKit pour se conformer au nouveau protocole de bibliothèque standard, vous pouvez facilement obtenir:... 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:
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
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 SwiftArray
(et il perd son type d'élément en revenant à Swift):Mais il est assez facile de faire un wrapper Swift préservant le type pour cela:
la source
let shuffled = lcg.arrayByShufflingObjects(in: array)
Dans Swift 2.0 , GameplayKit peut venir à la rescousse! (pris en charge par iOS9 ou version ultérieure)
la source
import GameplayKit.GKRandomSource
Voici peut-être un peu plus court:
la source
sort
fonction 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 :)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).collection.sorted { _,_ in arc4random_uniform(1) == 0 }
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é.
Maintenant, tout
MutableCollectionType
peut utiliser ces méthodes étant donné qu'il utiliseInt
commeIndex
la source
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.
la source
Il s'agit d'une version de l'implémentation par Nate du shuffle Fisher-Yates pour Swift 4 (Xcode 9).
Les changements sont les suivants:
Indices.Iterator.Element == Index
fait maintenant partie duCollection
protocole et n'a plus besoin d'être imposée à l'extension.swapAt()
à la collection, comparer SE-0173 AddMutableCollection.swapAt(_:_:)
.Element
est un alias pourIterator.Element
.la source
Voici ce que j'utilise:
la source
Swift 4 Mélangez les éléments d'un tableau dans une boucle for où i est le rapport de mélange
Ou avec l'extension Int
la source
Solution Swift 3, suite à la réponse de @Nate Cook: (fonctionne si l'index commence par 0, voir les commentaires ci-dessous)
la source
var a = [1, 2, 3, 4, 5, 6][3..<6]; a.shuffleInPlace()
plusieurs fois. - Voir stackoverflow.com/a/37843901/1187415 pour une solution correcte.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.Si vous souhaitez obtenir une chaîne mélangée à partir d'un tableau, vous pouvez utiliser le code ci-dessous.
la source
Avec Swift 3, si vous souhaitez mélanger un tableau en place ou obtenir un nouveau tableau mélangé à partir d'un tableau, cela
AnyIterator
peut vous aider. L'idée est de créer un tableau d'index à partir de votre tableau, de mélanger ces indices avec uneAnyIterator
instance et uneswap(_:_:)
fonction et de mapper chaque élément de cetteAnyIterator
instance avec l'élément correspondant du tableau.Le code Playground suivant montre comment cela fonctionne:
Vous pouvez refactoriser le code précédent et créer une
shuffled()
fonction à l'intérieur d'uneArray
extension afin d'obtenir un nouveau tableau mélangé à partir d'un tableau:Usage:
Comme alternative au code précédent, vous pouvez créer une
shuffle()
fonction à l'intérieur d'uneArray
extension afin de mélanger un tableau en place:Usage:
la source
Vous pouvez également utiliser la
swap
fonction générique et implémenter les Fisher-Yates mentionnés:ou moins verbeux:
la source
let rnd = Int(arc4random_uniform(UInt32(idx + 1)))
. De plus, dans FY, vous itérez généralement dearr.count - 1
bas en haut1
(ou si vous itérez de0
enarr.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.travaux!!. organismes est le tableau à mélanger.
la source
Dans Swift 4.2 , il existe désormais une méthode à la fois modifiable
shuffle
et immuableshuffled
. Vous pouvez en savoir plus sur la génération aléatoire et les éléments de tableau ici .la source
Voici comment mélanger un tableau avec une graine dans Swift 3.0.
la source
la source
Voici ce que j'utilise:
la source
Exemple simple:
la source
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):
Appel Shuffle sans mutation
[Array] -> [Array]
:Cela s'imprime
array
dans un ordre aléatoire.Appel mutant Shuffle
[Array] = [Array]
:Cela s'imprime
array
dans 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!
la source
Dans SWIFT 4
la source
Si vous souhaitez utiliser la fonction de boucle Swift For simple, utilisez ceci ->
Swift Array suffle en utilisant l'extension ->
la source
Depuis swift 4.2, il existe deux fonctions pratiques:
et
la source
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.
la source
drand48()
Donne également les mêmes nombres pseudo aléatoires à chaque fois, sauf si vous définissez une graine avec commesrand48(Int(arc4random()))
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'ajoute donc une condition comme ci-dessous
YA! C'est bon pour moi.
la source