Wie randomisiere oder mische ich die Elemente in einem Array in Swift? Zum Beispiel, wenn mein Array aus 52 Karten besteht, mag ich mische das Array , um das Deck zu mischen.
Dies ist nicht spezifisch für eine Sprache. Wenden Sie einfach einen beliebigen Mischalgorithmus an ...
Gabriele Petronella
8
@Mithrandir Das stimmt nicht. In Ruby würde man sich entscheiden array.shuffle. Sie müssen keine eigene Version implementieren. Ich denke, OP suchte nach etwas Ähnlichem.
Linus Oleander
1
Seien Sie jedoch vorsichtig, verwenden Sie nicht irgendeinen Shuffle-Algorithmus, um ein Kartenspiel zu mischen.
njzk2
Antworten:
627
Diese Antwort beschreibt, wie Sie mit einem schnellen und einheitlichen Algorithmus (Fisher-Yates) in Swift 4.2+ mischen und wie Sie dieselbe Funktion in den verschiedenen früheren Versionen von Swift hinzufügen. Die Benennung und das Verhalten für jede Swift-Version stimmen mit den mutierenden und nicht mutierenden Sortiermethoden für diese Version überein.
Swift 4.2+
shuffleund shuffledsind native ab Swift 4.2. Anwendungsbeispiel:
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 und 4.1
Diese Erweiterungen fügen shuffle()jeder veränderlichen Sammlung (Arrays und unsichere veränderbare Puffer) eine shuffled()Methode und jeder Sequenz eine Methode hinzu:
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
}}
Gleiche Verwendung wie in den obigen Swift 4.2-Beispielen.
Swift 3
Diese Erweiterungen fügen shuffle()jeder veränderlichen Sammlung eine shuffled()Methode und jeder Sequenz eine Methode hinzu:
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
}}
Gleiche Verwendung wie in den obigen Swift 4.2-Beispielen.
Swift 2
(veraltete Sprache: Sie können Swift 2.x nicht verwenden, um ab Juli 2018 auf iTunes Connect zu veröffentlichen.)
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
}}
Verwendungszweck:
[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
(veraltete Sprache: Sie können Swift 1.x nicht verwenden, um ab Juli 2018 auf iTunes Connect zu veröffentlichen.)
shuffle als mutierende Array-Methode
Mit dieser Erweiterung können Sie eine veränderbare ArrayInstanz an Ort und Stelle mischen :
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 als nicht mutierende Array-Methode
Mit dieser Erweiterung können Sie eine gemischte Kopie einer ArrayInstanz abrufen :
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]
Wenn Sie die Funktionsversion in Swift 1.2 möchten, muss sie ein wenig aktualisiert werden, da sie nicht mehr vorhanden countElementsist. Der Ersatz countgibt jetzt a zurück, T.Index.Distancesodass die Einschränkung aktiviert sein muss C.Index.Distance == Int. Diese Version sollte funktionieren: gist.github.com/airspeedswift/03d07a9dc86fabdc370f
Fluggeschwindigkeit Velocity
2
Dies ist die eigentliche Ausgabe. Fisher-Yates sollte eine unverzerrte zufällige Permutation der Quelle zurückgeben, sodass kein bestimmtes Element verschoben werden muss. Es gibt eine Garantie dafür, dass sich kein Element mehr als einmal bewegt, aber manchmal erfolgt die "Verschiebung" auf denselben Index. Der einfachste Fall ist, darüber nachzudenken - [1, 2].shuffled()sollte das [2, 1]jedes Mal zurückkehren?
Nate Cook
1
Ich habe if count > 0oben in der Funktion des mutierenden Arrays hinzugefügt , um zu verhindern, dass ein "schwerwiegender Fehler: Bereich mit Ende <Start" kann nicht gebildet werden, wenn ein leeres Array übergeben wird.
Carl Smith
3
@ Jan: Ja, guard i != j else { continue }vor dem Tausch hinzufügen . Ich habe ein Radar eingereicht, aber das neue Verhalten ist beabsichtigt.
Nate Cook
3
shuffleInPlaceKann tatsächlich abstürzen, wenn die Sammlungsindizes nicht bei Null beginnen, z. B. für ein Array-Slice. for i in 0..<count - 1 sollte sein for i in startIndex ..< endIndex - 1(und dann wird die Konvertierung zu Swift 3 fast trivial).
Martin R
131
Bearbeiten: Wie in anderen Antworten erwähnt, fügt Swift 4.2 der Standardbibliothek schließlich eine Zufallszahlengenerierung hinzu, einschließlich Array-Shuffling.
Die GKRandom/ GKRandomDistributionsuite in GameplayKit kann jedoch mit dem neuen RandomNumberGeneratorProtokoll weiterhin nützlich sein. Wenn Sie den GameplayKit-RNGs Erweiterungen hinzufügen, um sie an das neue Standardbibliotheksprotokoll anzupassen, erhalten Sie auf einfache Weise:
sendbare RNGs (die eine "zufällige" Sequenz reproduzieren können, wenn sie zum Testen benötigt werden)
RNGs, die Robustheit für Geschwindigkeit opfern
RNGs, die ungleichmäßige Verteilungen erzeugen
... und nutzen Sie trotzdem die netten neuen "nativen" Zufalls-APIs in Swift.
Der Rest dieser Antwort betrifft solche RNGs und / oder ihre Verwendung in älteren Swift-Compilern.
Hier gibt es bereits einige gute Antworten sowie einige gute Beispiele dafür, warum das Schreiben eines eigenen Shuffle fehleranfällig sein kann, wenn Sie nicht vorsichtig sind.
let shuffled =GKRandomSource.sharedRandom().arrayByShufflingObjects(in: array)
Wenn Sie in der Lage sein möchten, ein Mischen oder eine Reihe von Mischen zu replizieren, wählen Sie eine bestimmte zufällige Quelle aus und setzen Sie sie. z.B
let lcg =GKLinearCongruentialRandomSource(seed: mySeedValue)let shuffled = lcg.arrayByShufflingObjects(in: array)
In iOS 10 / macOS 10.12 / tvOS 10 gibt es auch eine praktische Syntax zum Mischen über eine Erweiterung NSArray. Das ist natürlich etwas umständlich, wenn Sie einen Swift verwenden Array(und er verliert seinen Elementtyp, wenn Sie zu Swift zurückkehren):
let shuffled1=(array asNSArray).shuffled(using: random)// -> [Any]
let shuffled2=(array asNSArray).shuffled()// use default random source
Aber es ist ziemlich einfach, einen typerhaltenden Swift-Wrapper dafür zu erstellen:
@moby Die sortFunktion benötigt einen Abschluss, um Elemente zu bestellen. Dieser Abschluss akzeptiert zwei Parameter (elem1, elem2) und muss true zurückgeben, wenn der erste Wert vor dem zweiten Wert erscheinen soll, andernfalls false. Wenn wir stattdessen einen zufälligen Booleschen Wert zurückgeben ... dann verwechseln wir einfach das Ganze :)
Jean Le Moignan
2
Gibt es hier einen Mathematiker, der dies bestätigt oder widerlegt?
Jean Le Moignan
9
Wie pjs als Antwort auf eine andere sehr ähnliche Antwort hervorhob, wird dies keine gleichmäßige Verteilung der Ergebnisse erzeugen. Verwenden Sie Fisher-Yates Shuffle wie in der Antwort von Nate Cook gezeigt.
Rob
1
Dies ist ein cleverer Trick, aber in Bezug auf die Qualität des Shuffle miserabel. Zum einen sollte dieser Verschluss verwendet werden arc4random_uniform(), da er derzeit der Modulo-Vorspannung unterliegt. Zweitens hängt die Ausgabe sehr stark vom Sortieralgorithmus ab (der uns ohne Blick auf die Quelle nicht bekannt ist).
Alexander - Reinstate Monica
1
Wenn man diesen einfacheren Ansatz fortsetzt, scheint dies recht gut zu funktionieren: collection.sorted { _,_ in arc4random_uniform(1) == 0 }
Markiv
7
Mit dem Algorithmus von Nate wollte ich sehen, wie dies mit Swift 2 und Protokollerweiterungen aussehen würde.
Das habe ich mir ausgedacht.
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
}}
Jetzt kann jeder MutableCollectionTypediese Methoden verwenden, vorausgesetzt, er verwendet IntalsIndex
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
}}
Die Änderungen sind:
Die Einschränkung Indices.Iterator.Element == Indexist jetzt Teil des CollectionProtokolls und muss der Erweiterung nicht mehr auferlegt werden.
Swift 4
Mische die Elemente eines Arrays in einer for-Schleife, wobei i das Mischungsverhältnis ist
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))))}}
Oder mit der Erweiterung 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}}}
Swift 3-Lösung, folgende Antwort von @Nate Cook: (Arbeiten, wenn der Index mit 0 beginnt, siehe Kommentare unten)
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])}}}
Dies kann abstürzen, wenn die Sammlungsindizes bei 0 beginnen, z. B. für ein Array-Slice. Versuche var a = [1, 2, 3, 4, 5, 6][3..<6]; a.shuffleInPlace()mehrmals zu laufen . - Eine korrekte Lösung finden Sie unter stackoverflow.com/a/37843901/1187415 .
Martin R
2
So wird es auf einfachste Weise gemacht. import Gamplaykitzu Ihrem VC und verwenden Sie den folgenden Code. Getestet in Xcode 8.
Wenn Sie mit Swift 3 ein Array an Ort und Stelle mischen oder ein neues gemischtes Array aus einem Array abrufen möchten, AnyIteratorkann dies hilfreich sein. Die Idee ist, ein Array von Indizes aus Ihrem Array zu erstellen, diese Indizes mit einer AnyIteratorInstanz und swap(_:_:)Funktion zu mischen und jedes Element dieser AnyIteratorInstanz dem entsprechenden Element des Arrays zuzuordnen .
Der folgende Spielplatzcode zeigt, wie es funktioniert:
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"]
Sie können den vorherigen Code umgestalten und eine shuffled()Funktion in einer ArrayErweiterung erstellen , um ein neues gemischtes Array aus einem Array zu erhalten:
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]}}}
Verwendungszweck:
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:[]
Alternativ zum vorherigen Code können Sie eine shuffle()Funktion in einer ArrayErweiterung erstellen , um ein Array an Ort und Stelle zu mischen:
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]}}}
Verwendungszweck:
var mutatingArray =["Jock","Ellie","Sue Ellen","Bobby","JR","Pamela"]
mutatingArray.shuffle()
print(mutatingArray)// may print ["Sue Ellen","Pamela","Jock","Ellie","Bobby","JR"]
Dies leidet zumindest an einem schwerwiegenden Fehler , der durch einen hier beschriebenen Fehler verursacht wird, bei dem ein Wert immer von seiner ursprünglichen Position ausgetauscht wird. Dies wird behoben mit let rnd = Int(arc4random_uniform(UInt32(idx + 1))). Außerdem iterieren Sie im Geschäftsjahr im Allgemeinen von arr.count - 1unten nach unten 1(oder wenn Sie von 0bis iterieren arr.count - 1, wählen Sie einen Index aus, wie Nate in der akzeptierten Antwort zeigt). Siehe Abschnitt Moderner Algorithmus in der Fisher-Yates-Diskussion.
Rob
1
funktioniert !!. Organismen ist das Array zu mischen.
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)")
In Swift 4.2 gibt es jetzt eine Methode sowohl für eine veränderlicheshuffle als auch für eine unveränderlicheshuffled . Weitere Informationen zur zufälligen Generierung und zum Array-Material finden Sie hier .
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]
Working Array Extension (mutierend und nicht mutierend)
Swift 4.1 / Xcode 9
Die Top-Antwort ist veraltet, daher habe ich es mir zur Aufgabe gemacht, eine eigene Erweiterung zu erstellen, um ein Array in der neuesten Version von Swift, Swift 4.1 (Xcode 9), zu mischen:
Nicht mutierendes Shuffle nennen [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)
Dies wird arrayin zufälliger Reihenfolge gedruckt .
Mutating Shuffle nennen [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)
Dies wird arrayin der aktuellen Reihenfolge gedruckt , die bereits zufällig gemischt wurde.
Ich hoffe, dass dies für alle funktioniert. Wenn Sie Fragen, Anregungen oder Kommentare haben, können Sie diese gerne stellen!
Dies ergibt eine ungleichmäßige Verteilung der Ergebnisse. Es wird auch O (n log n) sein, wobei ein Fisher-Yates-Shuffle in O (n) -Zeit gleichmäßig verteilte Ergebnisse liefern würde.
pjs
Auch drand48()gibt die gleiche Pseudo - Zufallszahlen jedes Mal, wenn Sie einen Samen mit wie eingestelltsrand48(Int(arc4random()))
Kametrixom
-3
Es stoppt bei "Swap (& self [i], & self [j])", wenn ich die xCode-Version auf 7.4 Beta aktualisiere.
Schwerwiegender Fehler: Das Austauschen eines Standorts mit sich selbst wird nicht unterstützt
Ich habe den Grund gefunden, dass i = j (Funktion des Swaps wird explodieren)
array.shuffle
. Sie müssen keine eigene Version implementieren. Ich denke, OP suchte nach etwas Ähnlichem.Antworten:
Diese Antwort beschreibt, wie Sie mit einem schnellen und einheitlichen Algorithmus (Fisher-Yates) in Swift 4.2+ mischen und wie Sie dieselbe Funktion in den verschiedenen früheren Versionen von Swift hinzufügen. Die Benennung und das Verhalten für jede Swift-Version stimmen mit den mutierenden und nicht mutierenden Sortiermethoden für diese Version überein.
Swift 4.2+
shuffle
undshuffled
sind native ab Swift 4.2. Anwendungsbeispiel:Swift 4.0 und 4.1
Diese Erweiterungen fügen
shuffle()
jeder veränderlichen Sammlung (Arrays und unsichere veränderbare Puffer) eineshuffled()
Methode und jeder Sequenz eine Methode hinzu:Gleiche Verwendung wie in den obigen Swift 4.2-Beispielen.
Swift 3
Diese Erweiterungen fügen
shuffle()
jeder veränderlichen Sammlung eineshuffled()
Methode und jeder Sequenz eine Methode hinzu:Gleiche Verwendung wie in den obigen Swift 4.2-Beispielen.
Swift 2
(veraltete Sprache: Sie können Swift 2.x nicht verwenden, um ab Juli 2018 auf iTunes Connect zu veröffentlichen.)
Verwendungszweck:
Swift 1.2
(veraltete Sprache: Sie können Swift 1.x nicht verwenden, um ab Juli 2018 auf iTunes Connect zu veröffentlichen.)
shuffle
als mutierende Array-MethodeMit dieser Erweiterung können Sie eine veränderbare
Array
Instanz an Ort und Stelle mischen :shuffled
als nicht mutierende Array-MethodeMit dieser Erweiterung können Sie eine gemischte Kopie einer
Array
Instanz abrufen :quelle
countElements
ist. Der Ersatzcount
gibt jetzt a zurück,T.Index.Distance
sodass die Einschränkung aktiviert sein mussC.Index.Distance == Int
. Diese Version sollte funktionieren: gist.github.com/airspeedswift/03d07a9dc86fabdc370f[1, 2].shuffled()
sollte das[2, 1]
jedes Mal zurückkehren?if count > 0
oben in der Funktion des mutierenden Arrays hinzugefügt , um zu verhindern, dass ein "schwerwiegender Fehler: Bereich mit Ende <Start" kann nicht gebildet werden, wenn ein leeres Array übergeben wird.guard i != j else { continue }
vor dem Tausch hinzufügen . Ich habe ein Radar eingereicht, aber das neue Verhalten ist beabsichtigt.shuffleInPlace
Kann tatsächlich abstürzen, wenn die Sammlungsindizes nicht bei Null beginnen, z. B. für ein Array-Slice.for i in 0..<count - 1
sollte seinfor i in startIndex ..< endIndex - 1
(und dann wird die Konvertierung zu Swift 3 fast trivial).Bearbeiten: Wie in anderen Antworten erwähnt, fügt Swift 4.2 der Standardbibliothek schließlich eine Zufallszahlengenerierung hinzu, einschließlich Array-Shuffling.
Die
GKRandom
/GKRandomDistribution
suite in GameplayKit kann jedoch mit dem neuenRandomNumberGenerator
Protokoll weiterhin nützlich sein. Wenn Sie den GameplayKit-RNGs Erweiterungen hinzufügen, um sie an das neue Standardbibliotheksprotokoll anzupassen, erhalten Sie auf einfache Weise:... und nutzen Sie trotzdem die netten neuen "nativen" Zufalls-APIs in Swift.
Der Rest dieser Antwort betrifft solche RNGs und / oder ihre Verwendung in älteren Swift-Compilern.
Hier gibt es bereits einige gute Antworten sowie einige gute Beispiele dafür, warum das Schreiben eines eigenen Shuffle fehleranfällig sein kann, wenn Sie nicht vorsichtig sind.
In iOS 9, macOS 10.11 und tvOS 9 (oder höher) müssen Sie keine eigenen schreiben. Es gibt eine effiziente, korrekte Implementierung von Fisher-Yates in GameplayKit (das trotz des Namens nicht nur für Spiele gedacht ist).
Wenn Sie nur ein einzigartiges Shuffle möchten:
Wenn Sie in der Lage sein möchten, ein Mischen oder eine Reihe von Mischen zu replizieren, wählen Sie eine bestimmte zufällige Quelle aus und setzen Sie sie. z.B
In iOS 10 / macOS 10.12 / tvOS 10 gibt es auch eine praktische Syntax zum Mischen über eine Erweiterung
NSArray
. Das ist natürlich etwas umständlich, wenn Sie einen Swift verwendenArray
(und er verliert seinen Elementtyp, wenn Sie zu Swift zurückkehren):Aber es ist ziemlich einfach, einen typerhaltenden Swift-Wrapper dafür zu erstellen:
quelle
let shuffled = lcg.arrayByShufflingObjects(in: array)
In Swift 2.0 kann GameplayKit Abhilfe schaffen! (unterstützt von iOS9 oder höher)
quelle
import GameplayKit.GKRandomSource
Hier ist etwas möglicherweise etwas kürzer:
quelle
sort
Funktion benötigt einen Abschluss, um Elemente zu bestellen. Dieser Abschluss akzeptiert zwei Parameter (elem1, elem2) und muss true zurückgeben, wenn der erste Wert vor dem zweiten Wert erscheinen soll, andernfalls false. Wenn wir stattdessen einen zufälligen Booleschen Wert zurückgeben ... dann verwechseln wir einfach das Ganze :)arc4random_uniform()
, da er derzeit der Modulo-Vorspannung unterliegt. Zweitens hängt die Ausgabe sehr stark vom Sortieralgorithmus ab (der uns ohne Blick auf die Quelle nicht bekannt ist).collection.sorted { _,_ in arc4random_uniform(1) == 0 }
Mit dem Algorithmus von Nate wollte ich sehen, wie dies mit Swift 2 und Protokollerweiterungen aussehen würde.
Das habe ich mir ausgedacht.
Jetzt kann jeder
MutableCollectionType
diese Methoden verwenden, vorausgesetzt, er verwendetInt
alsIndex
quelle
In meinem Fall hatte ich einige Probleme beim Austauschen von Objekten in Array. Dann kratzte ich mich am Kopf und erfand das Rad neu.
quelle
Dies ist eine Version von Nates Implementierung des Fisher-Yates-Shuffle für Swift 4 (Xcode 9).
Die Änderungen sind:
Indices.Iterator.Element == Index
ist jetzt Teil desCollection
Protokolls und muss der Erweiterung nicht mehr auferlegt werden.swapAt()
der Sammlung erfolgen, vergleiche SE-0173 HinzufügenMutableCollection.swapAt(_:_:)
.Element
ist ein Alias fürIterator.Element
.quelle
Das benutze ich:
quelle
Swift 4 Mische die Elemente eines Arrays in einer for-Schleife, wobei i das Mischungsverhältnis ist
Oder mit der Erweiterung Int
quelle
Swift 3-Lösung, folgende Antwort von @Nate Cook: (Arbeiten, wenn der Index mit 0 beginnt, siehe Kommentare unten)
quelle
var a = [1, 2, 3, 4, 5, 6][3..<6]; a.shuffleInPlace()
mehrmals zu laufen . - Eine korrekte Lösung finden Sie unter stackoverflow.com/a/37843901/1187415 .So wird es auf einfachste Weise gemacht.
import Gamplaykit
zu Ihrem VC und verwenden Sie den folgenden Code. Getestet in Xcode 8.Wenn Sie einen gemischten String aus einem Array erhalten möchten, können Sie den folgenden Code verwenden.
quelle
Wenn Sie mit Swift 3 ein Array an Ort und Stelle mischen oder ein neues gemischtes Array aus einem Array abrufen möchten,
AnyIterator
kann dies hilfreich sein. Die Idee ist, ein Array von Indizes aus Ihrem Array zu erstellen, diese Indizes mit einerAnyIterator
Instanz undswap(_:_:)
Funktion zu mischen und jedes Element dieserAnyIterator
Instanz dem entsprechenden Element des Arrays zuzuordnen .Der folgende Spielplatzcode zeigt, wie es funktioniert:
Sie können den vorherigen Code umgestalten und eine
shuffled()
Funktion in einerArray
Erweiterung erstellen , um ein neues gemischtes Array aus einem Array zu erhalten:Verwendungszweck:
Alternativ zum vorherigen Code können Sie eine
shuffle()
Funktion in einerArray
Erweiterung erstellen , um ein Array an Ort und Stelle zu mischen:Verwendungszweck:
quelle
Sie können auch die generische
swap
Funktion verwenden und die genannten Fisher-Yates implementieren:oder weniger ausführlich:
quelle
let rnd = Int(arc4random_uniform(UInt32(idx + 1)))
. Außerdem iterieren Sie im Geschäftsjahr im Allgemeinen vonarr.count - 1
unten nach unten1
(oder wenn Sie von0
bis iterierenarr.count - 1
, wählen Sie einen Index aus, wie Nate in der akzeptierten Antwort zeigt). Siehe Abschnitt Moderner Algorithmus in der Fisher-Yates-Diskussion.funktioniert !!. Organismen ist das Array zu mischen.
quelle
In Swift 4.2 gibt es jetzt eine Methode sowohl für eine veränderliche
shuffle
als auch für eine unveränderlicheshuffled
. Weitere Informationen zur zufälligen Generierung und zum Array-Material finden Sie hier .quelle
So mischen Sie ein Array mit einem Startwert in Swift 3.0.
quelle
quelle
Das benutze ich:
quelle
Einfaches Beispiel:
quelle
Working Array Extension (mutierend und nicht mutierend)
Swift 4.1 / Xcode 9
Die Top-Antwort ist veraltet, daher habe ich es mir zur Aufgabe gemacht, eine eigene Erweiterung zu erstellen, um ein Array in der neuesten Version von Swift, Swift 4.1 (Xcode 9), zu mischen:
Nicht mutierendes Shuffle nennen
[Array] -> [Array]
:Dies wird
array
in zufälliger Reihenfolge gedruckt .Mutating Shuffle nennen
[Array] = [Array]
:Dies wird
array
in der aktuellen Reihenfolge gedruckt , die bereits zufällig gemischt wurde.Ich hoffe, dass dies für alle funktioniert. Wenn Sie Fragen, Anregungen oder Kommentare haben, können Sie diese gerne stellen!
quelle
In SWIFT 4
quelle
Wenn Sie eine einfache Swift For-Schleifenfunktion verwenden möchten, verwenden Sie diese ->
Swift Array genügt mit der Erweiterung ->
quelle
Ab Swift 4.2 gibt es zwei praktische Funktionen:
und
quelle
Hier ist ein Code, der auf dem Spielplatz ausgeführt wird. Sie müssen Darwin nicht in ein aktuelles Xcode-Projekt importieren.
quelle
drand48()
gibt die gleiche Pseudo - Zufallszahlen jedes Mal, wenn Sie einen Samen mit wie eingestelltsrand48(Int(arc4random()))
Es stoppt bei "Swap (& self [i], & self [j])", wenn ich die xCode-Version auf 7.4 Beta aktualisiere.
Schwerwiegender Fehler: Das Austauschen eines Standorts mit sich selbst wird nicht unterstützt
Also füge ich eine Bedingung wie unten hinzu
YA! Für mich ist das in Ordnung.
quelle