Jak losowo lub tasować elementy w tablicy w Swift? Na przykład, jeśli moja tablica składa się z 52 kart do gry, chcę przetasować tablicę, aby przetasować talię.
nie jest to specyficzne dla żadnego języka. Po prostu zastosuj dowolny algorytm tasowania ...
Gabriele Petronella
8
@Mithrandir To nieprawda. W Ruby można by to zrobić array.shuffle. Nie ma potrzeby implementowania własnej wersji. Chyba OP szukał czegoś podobnego.
Linus Oleander
1
uważaj jednak, nie używaj żadnego algorytmu losowego do tasowania talii kart.
njzk2
Odpowiedzi:
626
Ta odpowiedź szczegółowo opisuje, jak tasować za pomocą szybkiego i jednolitego algorytmu (Fisher-Yates) w Swift 4.2+ oraz jak dodać tę samą funkcję w różnych poprzednich wersjach Swift. Nazwy i zachowanie każdej wersji Swift są zgodne z mutującymi i niemutującymi metodami sortowania dla tej wersji.
Swift 4.2+
shufflei shuffledsą rodzime od Swift 4.2. Przykładowe użycie:
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 i 4.1
Te rozszerzenia dodają shuffle()metodę do dowolnej kolekcji podlegającej mutacji (tablice i niebezpieczne bufory zmienne) oraz shuffled()metodę do dowolnej sekwencji:
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
}}
Takie samo użycie jak w powyższych przykładach Swift 4.2.
Szybki 3
Te rozszerzenia dodają shuffle()metodę do dowolnej kolekcji mutowalnej i shuffled()metodę do dowolnej sekwencji:
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
}}
Takie samo użycie jak w powyższych przykładach Swift 4.2.
Swift 2
(przestarzały język: nie można używać Swift 2.x do publikowania w iTunes Connect od lipca 2018 r.)
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
}}
Stosowanie:
[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
(przestarzały język: nie można używać Swift 1.x do publikowania w iTunes Connect od lipca 2018 r.)
shuffle jako metoda tablicy mutującej
To rozszerzenie pozwoli ci przetasować zmienną Arrayinstancję w miejscu:
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 jako niemodyfikująca metoda tablicowa
To rozszerzenie pozwala odzyskać przetasowaną kopię Arrayinstancji:
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]
Jeśli chcesz mieć wersję funkcji w Swift 1.2, wymaga ona trochę aktualizacji po jej countElementszniknięciu, a jej zastąpienie countzwraca teraz, T.Index.Distancewięc ograniczenie musi być włączone C.Index.Distance == Int. Ta wersja powinna działać: gist.github.com/airspeedswift/03d07a9dc86fabdc370f
Prędkość
2
To są rzeczywiste dane wyjściowe - Fisher-Yates powinien zwrócić obiektywną permutację źródła, więc nie ma wymogu, aby jakiś element się poruszał. Jest to gwarancja, że żaden element porusza się więcej niż raz, ale czasami „ruch” jest tym samym indeksie. Najprostszym przypadkiem jest zastanowienie się [1, 2].shuffled()- czy powinien wrócić za [2, 1]każdym razem?
Nate Cook
1
Dodałem if count > 0na górze funkcji mutującej tablicy, aby zapobiec otrzymaniu „błędu krytycznego: nie można utworzyć zakresu z końcem <start”, gdy zostanie przekazana pusta tablica.
Carl Smith
3
@ Jan: Tak, dodaj guard i != j else { continue }przed zamianą. Złożyłem radar, ale nowe zachowanie jest celowe.
Nate Cook,
3
W rzeczywistości shuffleInPlacemoże ulec awarii, jeśli indeksy kolekcji nie zaczynają się od zera, np. Dla wycinka tablicy. for i in 0..<count - 1 powinno być for i in startIndex ..< endIndex - 1(a wtedy konwersja do Swift 3 staje się prawie banalna).
Martin R
131
Edycja: Jak zauważono w innych odpowiedziach, Swift 4.2 w końcu dodaje generowanie liczb losowych do standardowej biblioteki wraz z tasowaniem tablicy.
Jednak GKRandom/ GKRandomDistributionSuite w GameplayKit może być nadal użyteczny z nowym RandomNumberGeneratorprotokołem - jeśli dodasz rozszerzenia do GameplayKit RNG w celu dostosowania do nowego standardowego protokołu biblioteki, możesz łatwo uzyskać:
wysyłane RNG (które mogą odtworzyć „losową” sekwencję, gdy jest to potrzebne do testowania)
RNG, które poświęcają solidność dla szybkości
RNG, które wytwarzają nierównomierne rozkłady
... i nadal korzystaj z nowych, „losowych” losowych interfejsów API w Swift.
Pozostała część odpowiedzi dotyczy takich RNG i / lub ich zastosowania w starszych kompilatorach Swift.
Jest już kilka dobrych odpowiedzi, a także kilka dobrych ilustracji, dlaczego pisanie własnego losowania może być podatne na błędy, jeśli nie jesteś ostrożny.
let shuffled =GKRandomSource.sharedRandom().arrayByShufflingObjects(in: array)
Jeśli chcesz być w stanie powielić losowanie lub serię losowania, wybierz i zapisz określone losowe źródło; na przykład
let lcg =GKLinearCongruentialRandomSource(seed: mySeedValue)let shuffled = lcg.arrayByShufflingObjects(in: array)
W iOS 10 / macOS 10.12 / tvOS 10 istnieje również wygodna składnia do przetasowania za pomocą rozszerzenia na NSArray. Oczywiście jest to trochę kłopotliwe, gdy używasz Swift Array(i traci swój typ elementu po powrocie do Swift):
let shuffled1=(array asNSArray).shuffled(using: random)// -> [Any]
let shuffled2=(array asNSArray).shuffled()// use default random source
Ale dość łatwo jest stworzyć dla niego opakowanie Swift zachowujące typ:
@moby sortFunkcja wymaga zamknięcia, aby zamówić elementy. To zamknięcie przyjmuje dwa parametry (elem1, elem2) i musi zwrócić true, jeśli pierwsza wartość powinna pojawić się przed drugą wartością, a false w przeciwnym razie. Jeśli zamiast tego zwrócimy losową wartość logiczną ... to po prostu wszystko pomieszamy :)
Jean Le Moignan
2
Czy jest tu jakiś matematyk, który mógłby potwierdzić lub obalić?
Jean Le Moignan,
9
Jak zauważyli pjs w odpowiedzi na inną bardzo podobną odpowiedź, nie spowoduje to jednolitego rozkładu wyników. Użyj Shuffle Fisher-Yatesa, jak pokazano w odpowiedzi Nate'a Cooka.
Rob
1
To sprytna sztuczka, ale jest fatalna pod względem jakości przetasowania. Po pierwsze, to zamknięcie powinno się zastosować arc4random_uniform(), ponieważ obecnie podlega ono tendencyjności modulo. Po drugie, wynik zależy bardzo mocno od algorytmu sortowania (który nie jest nam znany bez spojrzenia na źródło).
Alexander - Przywróć Monikę
1
Kontynuując to prostsze podejście, wydaje się, że działa całkiem nieźle: collection.sorted { _,_ in arc4random_uniform(1) == 0 }
markiv
7
Biorąc algorytm Nate'a, chciałem zobaczyć, jak by to wyglądało z Swift 2 i rozszerzeniami protokołu.
Właśnie to wymyśliłem.
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
}}
Teraz każdy MutableCollectionTypemoże korzystać z tych metod, ponieważ używa go IntjakoIndex
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
}}
Zmiany są następujące:
Ograniczenie Indices.Iterator.Element == Indexjest teraz częścią Collectionprotokołu i nie musi być już nakładane na rozszerzenie.
Swift 4
Przetasuj elementy tablicy w pętli for, w której i oznacza stosunek mieszania
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))))}}
Lub z rozszerzeniem 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}}}
Rozwiązanie Swift 3, zgodne z odpowiedzią @Nate Cook: (działa, jeśli indeks zaczyna się od 0, patrz komentarze poniżej)
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])}}}
Może się to zawieszać, jeśli indeksy kolekcji zaczynają się od 0, np. Dla wycinka tablicy. Spróbuj uruchomić var a = [1, 2, 3, 4, 5, 6][3..<6]; a.shuffleInPlace()kilka razy. - Aby znaleźć prawidłowe rozwiązanie, zobacz stackoverflow.com/a/37843901/1187415
Martin R
2
Tak to się robi w najprostszy sposób. import Gamplaykitdo swojego VC i użyj poniższego kodu. Testowane w Xcode 8.
W Swift 3, jeśli chcesz przetasować tablicę w miejscu lub uzyskać nową przetasowaną tablicę z tablicy, AnyIteratormoże ci pomóc. Chodzi o to, aby utworzyć tablicę indeksów z tablicy, przetasować te indeksy za pomocą AnyIteratorinstancji i swap(_:_:)funkcji oraz zmapować każdy element tego AnyIteratorwystąpienia za pomocą odpowiedniego elementu tablicy.
Poniższy kod placu zabaw pokazuje, jak to działa:
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"]
Możesz zmienić poprzedni kod i utworzyć shuffled()funkcję w Arrayrozszerzeniu, aby uzyskać nową tablicę losową z tablicy:
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]}}}
Stosowanie:
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:[]
Alternatywnie do poprzedniego kodu możesz utworzyć shuffle()funkcję wewnątrz Arrayrozszerzenia, aby przetasować tablicę w miejscu:
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]}}}
Stosowanie:
var mutatingArray =["Jock","Ellie","Sue Ellen","Bobby","JR","Pamela"]
mutatingArray.shuffle()
print(mutatingArray)// may print ["Sue Ellen","Pamela","Jock","Ellie","Bobby","JR"]
Cierpi na tym przynajmniej poważny błąd opisany tutaj jednym błędem , w wyniku którego wartość jest zawsze zamieniana z pierwotnej pozycji. Można temu zaradzić let rnd = Int(arc4random_uniform(UInt32(idx + 1))). Ponadto w FY generalnie iterujesz od arr.count - 1dołu do 1(lub jeśli iterujesz od 0do arr.count - 1, wybierasz indeks jak Nate pokazuje w zaakceptowanej odpowiedzi). Zobacz sekcję Modern Algorytm dyskusji Fisher-Yatesa.
Rob
1
Pracuje!!. organizmy to tablica do przetasowania.
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)")
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]
Rozszerzenie macierzy roboczej (mutowanie i niemutowanie)
Swift 4.1 / Xcode 9
Najlepsza odpowiedź jest przestarzała, więc postanowiłem stworzyć własne rozszerzenie, aby przetasować tablicę w najnowszej wersji 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)
To drukuje arrayw losowej kolejności.
Wywołanie losowego mutowania [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)
Drukuje arrayw bieżącej kolejności, która została już losowo przetasowana.
Mam nadzieję, że to zadziała dla wszystkich, jeśli masz jakieś pytania, sugestie lub komentarze, możesz je zadać!
Daje to nierównomierny rozkład wyników. Będzie to również O (n log n), gdzie losowanie Fisher-Yates dałoby równomiernie rozłożone wyniki w czasie O (n).
pjs
drand48()Daje również te same pseudolosowe liczby za każdym razem, chyba że ustawisz seed za pomocą likesrand48(Int(arc4random()))
Kametrixom
-3
Zatrzymuje się na „swap (i self [i], i self [j])”, kiedy aktualizuję wersję xCode do wersji 7.4 beta.
błąd krytyczny: zamiana lokalizacji z samym sobą nie jest obsługiwana
Znalazłem przyczynę, że i = j (funkcja zamiany wybuchnie)
array.shuffle
. Nie ma potrzeby implementowania własnej wersji. Chyba OP szukał czegoś podobnego.Odpowiedzi:
Ta odpowiedź szczegółowo opisuje, jak tasować za pomocą szybkiego i jednolitego algorytmu (Fisher-Yates) w Swift 4.2+ oraz jak dodać tę samą funkcję w różnych poprzednich wersjach Swift. Nazwy i zachowanie każdej wersji Swift są zgodne z mutującymi i niemutującymi metodami sortowania dla tej wersji.
Swift 4.2+
shuffle
ishuffled
są rodzime od Swift 4.2. Przykładowe użycie:Swift 4.0 i 4.1
Te rozszerzenia dodają
shuffle()
metodę do dowolnej kolekcji podlegającej mutacji (tablice i niebezpieczne bufory zmienne) orazshuffled()
metodę do dowolnej sekwencji:Takie samo użycie jak w powyższych przykładach Swift 4.2.
Szybki 3
Te rozszerzenia dodają
shuffle()
metodę do dowolnej kolekcji mutowalnej ishuffled()
metodę do dowolnej sekwencji:Takie samo użycie jak w powyższych przykładach Swift 4.2.
Swift 2
(przestarzały język: nie można używać Swift 2.x do publikowania w iTunes Connect od lipca 2018 r.)
Stosowanie:
Swift 1.2
(przestarzały język: nie można używać Swift 1.x do publikowania w iTunes Connect od lipca 2018 r.)
shuffle
jako metoda tablicy mutującejTo rozszerzenie pozwoli ci przetasować zmienną
Array
instancję w miejscu:shuffled
jako niemodyfikująca metoda tablicowaTo rozszerzenie pozwala odzyskać przetasowaną kopię
Array
instancji:źródło
countElements
zniknięciu, a jej zastąpieniecount
zwraca teraz,T.Index.Distance
więc ograniczenie musi być włączoneC.Index.Distance == Int
. Ta wersja powinna działać: gist.github.com/airspeedswift/03d07a9dc86fabdc370f[1, 2].shuffled()
- czy powinien wrócić za[2, 1]
każdym razem?if count > 0
na górze funkcji mutującej tablicy, aby zapobiec otrzymaniu „błędu krytycznego: nie można utworzyć zakresu z końcem <start”, gdy zostanie przekazana pusta tablica.guard i != j else { continue }
przed zamianą. Złożyłem radar, ale nowe zachowanie jest celowe.shuffleInPlace
może ulec awarii, jeśli indeksy kolekcji nie zaczynają się od zera, np. Dla wycinka tablicy.for i in 0..<count - 1
powinno byćfor i in startIndex ..< endIndex - 1
(a wtedy konwersja do Swift 3 staje się prawie banalna).Edycja: Jak zauważono w innych odpowiedziach, Swift 4.2 w końcu dodaje generowanie liczb losowych do standardowej biblioteki wraz z tasowaniem tablicy.
Jednak
GKRandom
/GKRandomDistribution
Suite w GameplayKit może być nadal użyteczny z nowymRandomNumberGenerator
protokołem - jeśli dodasz rozszerzenia do GameplayKit RNG w celu dostosowania do nowego standardowego protokołu biblioteki, możesz łatwo uzyskać:... i nadal korzystaj z nowych, „losowych” losowych interfejsów API w Swift.
Pozostała część odpowiedzi dotyczy takich RNG i / lub ich zastosowania w starszych kompilatorach Swift.
Jest już kilka dobrych odpowiedzi, a także kilka dobrych ilustracji, dlaczego pisanie własnego losowania może być podatne na błędy, jeśli nie jesteś ostrożny.
W iOS 9, macOS 10.11 i tvOS 9 (lub nowszy) nie musisz pisać własnego. W GameplayKit istnieje wydajna, poprawna implementacja Fisher-Yatesa (która, pomimo nazwy, nie jest przeznaczona tylko do gier).
Jeśli chcesz tylko wyjątkowego losowania:
Jeśli chcesz być w stanie powielić losowanie lub serię losowania, wybierz i zapisz określone losowe źródło; na przykład
W iOS 10 / macOS 10.12 / tvOS 10 istnieje również wygodna składnia do przetasowania za pomocą rozszerzenia na
NSArray
. Oczywiście jest to trochę kłopotliwe, gdy używasz SwiftArray
(i traci swój typ elementu po powrocie do Swift):Ale dość łatwo jest stworzyć dla niego opakowanie Swift zachowujące typ:
źródło
let shuffled = lcg.arrayByShufflingObjects(in: array)
W Swift 2.0 na pomoc może przyjść GameplayKit! (obsługiwany przez iOS9 lub nowszy)
źródło
import GameplayKit.GKRandomSource
Oto coś, być może nieco krótszego:
źródło
sort
Funkcja wymaga zamknięcia, aby zamówić elementy. To zamknięcie przyjmuje dwa parametry (elem1, elem2) i musi zwrócić true, jeśli pierwsza wartość powinna pojawić się przed drugą wartością, a false w przeciwnym razie. Jeśli zamiast tego zwrócimy losową wartość logiczną ... to po prostu wszystko pomieszamy :)arc4random_uniform()
, ponieważ obecnie podlega ono tendencyjności modulo. Po drugie, wynik zależy bardzo mocno od algorytmu sortowania (który nie jest nam znany bez spojrzenia na źródło).collection.sorted { _,_ in arc4random_uniform(1) == 0 }
Biorąc algorytm Nate'a, chciałem zobaczyć, jak by to wyglądało z Swift 2 i rozszerzeniami protokołu.
Właśnie to wymyśliłem.
Teraz każdy
MutableCollectionType
może korzystać z tych metod, ponieważ używa goInt
jakoIndex
źródło
W moim przypadku miałem problemy z zamianą obiektów w szyku. Potem podrapałem się w głowę i postanowiłem odkryć koło.
źródło
To jest wersja implementacji losowania Fisher-Yates dla Swift 4 (Xcode 9) przez Nate'a .
Zmiany są następujące:
Indices.Iterator.Element == Index
jest teraz częściąCollection
protokołu i nie musi być już nakładane na rozszerzenie.swapAt()
kolekcję, porównaj SE-0173 DodajMutableCollection.swapAt(_:_:)
.Element
jest pseudonimem dlaIterator.Element
.źródło
Oto, czego używam:
źródło
Swift 4 Przetasuj elementy tablicy w pętli for, w której i oznacza stosunek mieszania
Lub z rozszerzeniem Int
źródło
Rozwiązanie Swift 3, zgodne z odpowiedzią @Nate Cook: (działa, jeśli indeks zaczyna się od 0, patrz komentarze poniżej)
źródło
var a = [1, 2, 3, 4, 5, 6][3..<6]; a.shuffleInPlace()
kilka razy. - Aby znaleźć prawidłowe rozwiązanie, zobacz stackoverflow.com/a/37843901/1187415Tak to się robi w najprostszy sposób.
import Gamplaykit
do swojego VC i użyj poniższego kodu. Testowane w Xcode 8.Jeśli chcesz uzyskać tasowany ciąg znaków z tablicy, możesz użyć poniższego kodu.
źródło
W Swift 3, jeśli chcesz przetasować tablicę w miejscu lub uzyskać nową przetasowaną tablicę z tablicy,
AnyIterator
może ci pomóc. Chodzi o to, aby utworzyć tablicę indeksów z tablicy, przetasować te indeksy za pomocąAnyIterator
instancji iswap(_:_:)
funkcji oraz zmapować każdy element tegoAnyIterator
wystąpienia za pomocą odpowiedniego elementu tablicy.Poniższy kod placu zabaw pokazuje, jak to działa:
Możesz zmienić poprzedni kod i utworzyć
shuffled()
funkcję wArray
rozszerzeniu, aby uzyskać nową tablicę losową z tablicy:Stosowanie:
Alternatywnie do poprzedniego kodu możesz utworzyć
shuffle()
funkcję wewnątrzArray
rozszerzenia, aby przetasować tablicę w miejscu:Stosowanie:
źródło
Możesz także użyć
swap
funkcji ogólnej i zaimplementować wspomnianego Fisher-Yatesa:lub mniej gadatliwie:
źródło
let rnd = Int(arc4random_uniform(UInt32(idx + 1)))
. Ponadto w FY generalnie iterujesz odarr.count - 1
dołu do1
(lub jeśli iterujesz od0
doarr.count - 1
, wybierasz indeks jak Nate pokazuje w zaakceptowanej odpowiedzi). Zobacz sekcję Modern Algorytm dyskusji Fisher-Yatesa.Pracuje!!. organizmy to tablica do przetasowania.
źródło
W Swift 4.2 dostępna jest teraz metoda zarówno zmienna, jak
shuffle
i niezmiennashuffled
. Możesz przeczytać więcej o losowym generowaniu i tablicach tutaj .źródło
Oto jak przetasować jedną tablicę z ziarnem w Swift 3.0.
źródło
źródło
Oto, czego używam:
źródło
Prosty przykład:
źródło
Rozszerzenie macierzy roboczej (mutowanie i niemutowanie)
Swift 4.1 / Xcode 9
Najlepsza odpowiedź jest przestarzała, więc postanowiłem stworzyć własne rozszerzenie, aby przetasować tablicę w najnowszej wersji Swift, Swift 4.1 (Xcode 9):
Zadzwoń bez mutacji
[Array] -> [Array]
:To drukuje
array
w losowej kolejności.Wywołanie losowego mutowania
[Array] = [Array]
:Drukuje
array
w bieżącej kolejności, która została już losowo przetasowana.Mam nadzieję, że to zadziała dla wszystkich, jeśli masz jakieś pytania, sugestie lub komentarze, możesz je zadać!
źródło
W SWIFT 4
źródło
Jeśli chcesz użyć prostej funkcji pętli Swift For, użyj tego ->
Swift Array suffle przy użyciu rozszerzenia ->
źródło
Począwszy od wersji 4.2, są dwie przydatne funkcje:
i
źródło
Oto kod działający na placu zabaw. Nie musisz importować Darwina do rzeczywistego projektu Xcode.
źródło
drand48()
Daje również te same pseudolosowe liczby za każdym razem, chyba że ustawisz seed za pomocą likesrand48(Int(arc4random()))
Zatrzymuje się na „swap (i self [i], i self [j])”, kiedy aktualizuję wersję xCode do wersji 7.4 beta.
błąd krytyczny: zamiana lokalizacji z samym sobą nie jest obsługiwana
Więc dodaję warunek jak poniżej
YA! Dla mnie ok.
źródło