Bagaimana cara mengacak atau mengacak elemen dalam array di Swift? Sebagai contoh, jika array saya terdiri dari 52 kartu remi, saya ingin mengocok array untuk mengocok deck.
ini tidak spesifik untuk bahasa apa pun. Cukup terapkan algoritma pengacakan apa pun ...
Gabriele Petronella
8
@Mithrandir Itu tidak benar. Di Ruby satu akan pergi untuk array.shuffle. Tidak perlu menerapkan versi Anda sendiri. Saya kira OP sedang mencari sesuatu yang serupa.
Linus Oleander
1
berhati-hatilah, jangan gunakan sembarang algoritma acak untuk mengocok setumpuk kartu.
njzk2
Jawaban:
627
Jawaban ini merinci cara mengocok dengan algoritma cepat dan seragam (Fisher-Yates) di Swift 4.2+ dan cara menambahkan fitur yang sama di berbagai versi Swift sebelumnya. Penamaan dan perilaku untuk setiap versi Swift cocok dengan metode penyortiran yang bermutasi dan nonmutasi untuk versi itu.
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 dan 4.1
Ekstensi ini menambahkan shuffle()metode ke koleksi yang dapat diubah (array dan buffer yang tidak bisa diubah) dan shuffled()metode untuk urutan apa pun:
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
}}
Penggunaan yang sama seperti pada contoh Swift 4.2 di atas.
Cepat 3
Ekstensi ini menambahkan shuffle()metode ke koleksi yang dapat diubah dan shuffled()metode ke urutan apa pun:
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
}}
Penggunaan yang sama seperti pada contoh Swift 4.2 di atas.
Cepat 2
(bahasa usang: Anda tidak dapat menggunakan Swift 2.x untuk menerbitkan di iTunes Connect mulai Juli 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
}}
Pemakaian:
[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]
Cepat 1.2
(bahasa usang: Anda tidak dapat menggunakan Swift 1.x untuk menerbitkan di iTunes Connect mulai Juli 2018)
shuffle sebagai metode array bermutasi
Ekstensi ini akan memungkinkan Anda mengocok Arrayinstance yang bisa berubah di tempat:
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 sebagai metode array yang tidak bermutasi
Ekstensi ini akan memungkinkan Anda mengambil salinan Arraycontoh yang dikocok :
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]
Jika Anda menginginkan versi fungsi di Swift 1.2, perlu sedikit pembaruan seperti yang countElementssudah hilang, dan itu penggantian count,, sekarang mengembalikan T.Index.Distancesehingga kendala perlu dihidupkan C.Index.Distance == Int. Versi ini akan berfungsi: gist.github.com/airspeedswift/03d07a9dc86fabdc370f
Airspeed Velocity
2
Itu adalah hasil aktual — Fisher-Yates harus mengembalikan permutasi acak yang tidak bias dari sumber, jadi tidak ada persyaratan bahwa elemen tertentu harus bergerak. Ada adalah jaminan bahwa tidak ada unsur bergerak lebih dari sekali, tapi kadang-kadang "bergerak" adalah untuk indeks yang sama. Kasus paling sederhana adalah memikirkan — apakah [1, 2].shuffled()itu akan kembali [2, 1]setiap waktu?
Nate Cook
1
Saya menambahkan if count > 0di bagian atas fungsi array yang bermutasi, untuk mencegah menerima "kesalahan fatal: Tidak dapat membentuk Rentang dengan akhir <mulai" ketika dilewatkan array kosong.
Carl Smith
3
@ Jan: Ya, tambahkan guard i != j else { continue }sebelum swap. Saya mengajukan radar, tetapi perilaku baru itu disengaja.
Nate Cook
3
Sebenarnya shuffleInPlacebisa crash jika indeks koleksi tidak dimulai dari nol, misalnya untuk slice array. for i in 0..<count - 1 seharusnya for i in startIndex ..< endIndex - 1(dan kemudian konversi ke Swift 3 menjadi hampir sepele).
Martin R
131
Sunting: Sebagaimana dicatat dalam jawaban lain, Swift 4.2 akhirnya menambahkan pembuatan angka acak ke pustaka standar, lengkap dengan pengocokan array.
Namun, GKRandom/ GKRandomDistributionsuite di GameplayKit masih dapat bermanfaat dengan RandomNumberGeneratorprotokol baru - jika Anda menambahkan ekstensi ke GameplayKit RNGs agar sesuai dengan protokol perpustakaan standar baru, Anda dapat dengan mudah mendapatkan:
RNG yang dapat dikirim (yang dapat mereproduksi urutan "acak" saat diperlukan untuk pengujian)
RNG yang mengorbankan ketahanan untuk kecepatan
RNG yang menghasilkan distribusi tidak seragam
... dan masih menggunakan API acak "asli" baru yang bagus di Swift.
Sisa jawaban ini menyangkut RNG dan / atau penggunaannya dalam kompiler Swift yang lebih lama.
Sudah ada beberapa jawaban bagus di sini, serta beberapa ilustrasi bagus mengapa menulis shuffle Anda sendiri bisa rawan kesalahan jika Anda tidak hati-hati.
Di iOS 9, macOS 10.11, dan tvOS 9 (atau lebih baru), Anda tidak harus menulis sendiri. Ada implementasi Fisher-Yates yang efisien dan benar di GameplayKit (yang, terlepas dari namanya, tidak hanya untuk game).
Jika Anda hanya ingin pengocokan unik:
let shuffled =GKRandomSource.sharedRandom().arrayByShufflingObjects(in: array)
Jika Anda ingin dapat mereplikasi shuffle atau serangkaian shuffles, pilih dan buang sumber acak tertentu; misalnya
let lcg =GKLinearCongruentialRandomSource(seed: mySeedValue)let shuffled = lcg.arrayByShufflingObjects(in: array)
Di iOS 10 / macOS 10.12 / tvOS 10, ada juga sintaksis kenyamanan untuk mengacak melalui ekstensi aktif NSArray. Tentu saja, itu sedikit rumit ketika Anda menggunakan Swift Array(dan kehilangan tipe elemennya saat kembali ke Swift):
let shuffled1=(array asNSArray).shuffled(using: random)// -> [Any]
let shuffled2=(array asNSArray).shuffled()// use default random source
Tapi cukup mudah untuk membuat pembungkus Swift tipe pengawet untuk itu:
@moby sortFungsi ini membutuhkan penutupan untuk memesan elemen. Penutupan ini mengambil dua parameter (elem1, elem2) dan harus mengembalikan true jika nilai pertama harus muncul sebelum nilai kedua, dan false jika tidak. Jika kita mengembalikan boolean acak sebagai gantinya ... maka kita hanya mencampur semuanya :)
Jean Le Moignan
2
Adakah ahli matematika di sini untuk mengonfirmasi atau membantah?
Jean Le Moignan
9
Seperti yang ditunjukkan oleh pj dalam menanggapi jawaban lain yang sangat mirip, ini tidak akan menghasilkan distribusi hasil yang seragam. Gunakan Shuffle Fisher-Yates seperti yang ditunjukkan dalam jawaban Nate Cook.
Rob
1
Ini adalah trik yang cerdas, tetapi buruk dalam hal kualitas shuffle. Untuk satu, penutupan ini harus digunakan arc4random_uniform(), karena saat ini tunduk pada bias modulo. Kedua, output sangat tergantung pada algoritma pengurutan (yang tidak kita ketahui tanpa melihat sumbernya).
Alexander - Reinstate Monica
1
Melanjutkan dengan pendekatan yang lebih sederhana ini, ini tampaknya bekerja dengan cukup baik: collection.sorted { _,_ in arc4random_uniform(1) == 0 }
markiv
7
Mengambil algoritma Nate saya ingin melihat bagaimana ini akan terlihat dengan Swift 2 dan ekstensi protokol.
Inilah yang saya pikirkan.
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
}}
Sekarang, siapa pun MutableCollectionTypedapat menggunakan metode ini mengingat itu digunakan IntsebagaiIndex
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
}}
Perubahannya adalah:
Batasan Indices.Iterator.Element == Indexsekarang menjadi bagian dari Collectionprotokol, dan tidak perlu dikenakan pada ekstensi lagi.
Swift 4
Kocok elemen-elemen array dalam for for loop di mana i adalah rasio pencampuran
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))))}}
Atau dengan ekstensi 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}}}
Solusi Swift 3, berikut @Nate Cook menjawab: (berfungsi jika indeks dimulai dengan 0, lihat komentar di bawah)
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])}}}
Ini bisa macet jika indeks koleksi mulai dari 0, misalnya untuk slice array. Coba jalankan var a = [1, 2, 3, 4, 5, 6][3..<6]; a.shuffleInPlace()beberapa kali. - Lihat stackoverflow.com/a/37843901/1187415 untuk solusi yang benar.
Martin R
2
Ini adalah bagaimana ini dilakukan dengan cara yang paling sederhana. import Gamplaykitke VC Anda dan gunakan kode di bawah ini. Diuji dalam Xcode 8.
Dengan Swift 3, jika Anda ingin mengocok array di tempatnya atau mendapatkan array yang diacak dari array, AnyIteratordapat membantu Anda. Idenya adalah untuk membuat array indeks dari array Anda, untuk mengocok indeks tersebut dengan AnyIteratorinstance dan swap(_:_:)fungsi dan untuk memetakan setiap elemen dari AnyIteratorinstance ini dengan elemen yang sesuai array.
Kode Playground berikut menunjukkan cara kerjanya:
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"]
Anda bisa memperbaiki kode sebelumnya dan membuat shuffled()fungsi di dalam Arrayekstensi untuk mendapatkan array yang diacak dari array:
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]}}}
Pemakaian:
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:[]
Sebagai alternatif dari kode sebelumnya, Anda dapat membuat shuffle()fungsi di dalam Arrayekstensi untuk mengocok array di tempat:
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]}}}
Pemakaian:
var mutatingArray =["Jock","Ellie","Sue Ellen","Bobby","JR","Pamela"]
mutatingArray.shuffle()
print(mutatingArray)// may print ["Sue Ellen","Pamela","Jock","Ellie","Bobby","JR"]
Ini menderita, paling tidak, kesalahan serius oleh satu kesalahan yang dijelaskan di sini di mana nilai selalu diganti dari posisi aslinya. Ini diperbaiki dengan let rnd = Int(arc4random_uniform(UInt32(idx + 1))). Juga, di TA, Anda biasanya beralih dari arr.count - 1bawah ke 1(atau jika Anda beralih dari 0ke arr.count - 1, Anda memilih indeks seperti Nate menunjukkan dalam jawaban yang diterima). Lihat bagian Algoritma Modern dari diskusi Fisher-Yates.
Rob
1
berhasil !!. organisme adalah array untuk diacak.
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]
Extension Array yang Berfungsi (bermutasi & tidak bermutasi)
Swift 4.1 / Xcode 9
Jawaban teratas sudah usang, jadi saya mengambil sendiri untuk membuat ekstensi sendiri untuk mengocok array dalam versi terbaru 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)
Ini mencetak arraydalam urutan acak.
Panggil Mutating 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)
Ini mencetak arraydalam urutan saat ini, yang telah dikocok secara acak.
Berharap ini bekerja untuk semua orang, jika Anda memiliki pertanyaan, saran, atau komentar, jangan ragu untuk bertanya!
Ini memberikan distribusi hasil yang tidak seragam. Ini juga akan menjadi O (n log n), di mana shuffle Fisher-Yates akan memberikan hasil yang terdistribusi secara merata dalam waktu O (n).
pjs
Juga drand48()memberikan angka acak semu yang sama setiap kali, kecuali jika Anda menetapkan seed dengan likesrand48(Int(arc4random()))
Kametrixom
-3
Itu berhenti di "swap (& self [i], & self [j])" ketika saya meningkatkan versi xCode ke 7.4 beta.
kesalahan fatal: menukar lokasi dengan dirinya sendiri tidak didukung
Saya menemukan alasan bahwa i = j (fungsi swap akan meledak)
array.shuffle
. Tidak perlu menerapkan versi Anda sendiri. Saya kira OP sedang mencari sesuatu yang serupa.Jawaban:
Jawaban ini merinci cara mengocok dengan algoritma cepat dan seragam (Fisher-Yates) di Swift 4.2+ dan cara menambahkan fitur yang sama di berbagai versi Swift sebelumnya. Penamaan dan perilaku untuk setiap versi Swift cocok dengan metode penyortiran yang bermutasi dan nonmutasi untuk versi itu.
Swift 4.2+
shuffle
danshuffled
asli mulai Swift 4.2. Contoh penggunaan:Swift 4.0 dan 4.1
Ekstensi ini menambahkan
shuffle()
metode ke koleksi yang dapat diubah (array dan buffer yang tidak bisa diubah) danshuffled()
metode untuk urutan apa pun:Penggunaan yang sama seperti pada contoh Swift 4.2 di atas.
Cepat 3
Ekstensi ini menambahkan
shuffle()
metode ke koleksi yang dapat diubah danshuffled()
metode ke urutan apa pun:Penggunaan yang sama seperti pada contoh Swift 4.2 di atas.
Cepat 2
(bahasa usang: Anda tidak dapat menggunakan Swift 2.x untuk menerbitkan di iTunes Connect mulai Juli 2018)
Pemakaian:
Cepat 1.2
(bahasa usang: Anda tidak dapat menggunakan Swift 1.x untuk menerbitkan di iTunes Connect mulai Juli 2018)
shuffle
sebagai metode array bermutasiEkstensi ini akan memungkinkan Anda mengocok
Array
instance yang bisa berubah di tempat:shuffled
sebagai metode array yang tidak bermutasiEkstensi ini akan memungkinkan Anda mengambil salinan
Array
contoh yang dikocok :sumber
countElements
sudah hilang, dan itu penggantiancount
,, sekarang mengembalikanT.Index.Distance
sehingga kendala perlu dihidupkanC.Index.Distance == Int
. Versi ini akan berfungsi: gist.github.com/airspeedswift/03d07a9dc86fabdc370f[1, 2].shuffled()
itu akan kembali[2, 1]
setiap waktu?if count > 0
di bagian atas fungsi array yang bermutasi, untuk mencegah menerima "kesalahan fatal: Tidak dapat membentuk Rentang dengan akhir <mulai" ketika dilewatkan array kosong.guard i != j else { continue }
sebelum swap. Saya mengajukan radar, tetapi perilaku baru itu disengaja.shuffleInPlace
bisa crash jika indeks koleksi tidak dimulai dari nol, misalnya untuk slice array.for i in 0..<count - 1
seharusnyafor i in startIndex ..< endIndex - 1
(dan kemudian konversi ke Swift 3 menjadi hampir sepele).Sunting: Sebagaimana dicatat dalam jawaban lain, Swift 4.2 akhirnya menambahkan pembuatan angka acak ke pustaka standar, lengkap dengan pengocokan array.
Namun,
GKRandom
/GKRandomDistribution
suite di GameplayKit masih dapat bermanfaat denganRandomNumberGenerator
protokol baru - jika Anda menambahkan ekstensi ke GameplayKit RNGs agar sesuai dengan protokol perpustakaan standar baru, Anda dapat dengan mudah mendapatkan:... dan masih menggunakan API acak "asli" baru yang bagus di Swift.
Sisa jawaban ini menyangkut RNG dan / atau penggunaannya dalam kompiler Swift yang lebih lama.
Sudah ada beberapa jawaban bagus di sini, serta beberapa ilustrasi bagus mengapa menulis shuffle Anda sendiri bisa rawan kesalahan jika Anda tidak hati-hati.
Di iOS 9, macOS 10.11, dan tvOS 9 (atau lebih baru), Anda tidak harus menulis sendiri. Ada implementasi Fisher-Yates yang efisien dan benar di GameplayKit (yang, terlepas dari namanya, tidak hanya untuk game).
Jika Anda hanya ingin pengocokan unik:
Jika Anda ingin dapat mereplikasi shuffle atau serangkaian shuffles, pilih dan buang sumber acak tertentu; misalnya
Di iOS 10 / macOS 10.12 / tvOS 10, ada juga sintaksis kenyamanan untuk mengacak melalui ekstensi aktif
NSArray
. Tentu saja, itu sedikit rumit ketika Anda menggunakan SwiftArray
(dan kehilangan tipe elemennya saat kembali ke Swift):Tapi cukup mudah untuk membuat pembungkus Swift tipe pengawet untuk itu:
sumber
let shuffled = lcg.arrayByShufflingObjects(in: array)
Di Swift 2.0 , GameplayKit bisa menyelamatkan! (didukung oleh iOS9 atau yang lebih baru)
sumber
import GameplayKit.GKRandomSource
Berikut ini sesuatu yang mungkin sedikit lebih pendek:
sumber
sort
Fungsi ini membutuhkan penutupan untuk memesan elemen. Penutupan ini mengambil dua parameter (elem1, elem2) dan harus mengembalikan true jika nilai pertama harus muncul sebelum nilai kedua, dan false jika tidak. Jika kita mengembalikan boolean acak sebagai gantinya ... maka kita hanya mencampur semuanya :)arc4random_uniform()
, karena saat ini tunduk pada bias modulo. Kedua, output sangat tergantung pada algoritma pengurutan (yang tidak kita ketahui tanpa melihat sumbernya).collection.sorted { _,_ in arc4random_uniform(1) == 0 }
Mengambil algoritma Nate saya ingin melihat bagaimana ini akan terlihat dengan Swift 2 dan ekstensi protokol.
Inilah yang saya pikirkan.
Sekarang, siapa pun
MutableCollectionType
dapat menggunakan metode ini mengingat itu digunakanInt
sebagaiIndex
sumber
Dalam kasus saya, saya punya beberapa masalah dengan menukar objek di Array. Lalu aku menggaruk kepalaku dan pergi untuk menemukan kembali rodanya.
sumber
Ini adalah versi implementasi Nate dari shuffle Fisher-Yates untuk Swift 4 (Xcode 9).
Perubahannya adalah:
Indices.Iterator.Element == Index
sekarang menjadi bagian dariCollection
protokol, dan tidak perlu dikenakan pada ekstensi lagi.swapAt()
koleksi, bandingkan SE-0173 TambahMutableCollection.swapAt(_:_:)
.Element
adalah alias untukIterator.Element
.sumber
Inilah yang saya gunakan:
sumber
Swift 4 Kocok elemen-elemen array dalam for for loop di mana i adalah rasio pencampuran
Atau dengan ekstensi Int
sumber
Solusi Swift 3, berikut @Nate Cook menjawab: (berfungsi jika indeks dimulai dengan 0, lihat komentar di bawah)
sumber
var a = [1, 2, 3, 4, 5, 6][3..<6]; a.shuffleInPlace()
beberapa kali. - Lihat stackoverflow.com/a/37843901/1187415 untuk solusi yang benar.Ini adalah bagaimana ini dilakukan dengan cara yang paling sederhana.
import Gamplaykit
ke VC Anda dan gunakan kode di bawah ini. Diuji dalam Xcode 8.Jika Anda ingin mendapatkan String yang diacak dari Array, Anda dapat menggunakan kode di bawah ini ..
sumber
Dengan Swift 3, jika Anda ingin mengocok array di tempatnya atau mendapatkan array yang diacak dari array,
AnyIterator
dapat membantu Anda. Idenya adalah untuk membuat array indeks dari array Anda, untuk mengocok indeks tersebut denganAnyIterator
instance danswap(_:_:)
fungsi dan untuk memetakan setiap elemen dariAnyIterator
instance ini dengan elemen yang sesuai array.Kode Playground berikut menunjukkan cara kerjanya:
Anda bisa memperbaiki kode sebelumnya dan membuat
shuffled()
fungsi di dalamArray
ekstensi untuk mendapatkan array yang diacak dari array:Pemakaian:
Sebagai alternatif dari kode sebelumnya, Anda dapat membuat
shuffle()
fungsi di dalamArray
ekstensi untuk mengocok array di tempat:Pemakaian:
sumber
Anda dapat menggunakan
swap
fungsi generik juga dan mengimplementasikan Fisher-Yates yang disebutkan:atau kurang verbose:
sumber
let rnd = Int(arc4random_uniform(UInt32(idx + 1)))
. Juga, di TA, Anda biasanya beralih dariarr.count - 1
bawah ke1
(atau jika Anda beralih dari0
kearr.count - 1
, Anda memilih indeks seperti Nate menunjukkan dalam jawaban yang diterima). Lihat bagian Algoritma Modern dari diskusi Fisher-Yates.berhasil !!. organisme adalah array untuk diacak.
sumber
Di Swift 4.2 , sekarang ada metode untuk bisa berubah
shuffle
dan tidak berubahshuffled
. Anda dapat membaca lebih lanjut tentang generasi acak dan berbagai hal di sini .sumber
Ini adalah cara mengocok satu array dengan seed di Swift 3.0.
sumber
sumber
Inilah yang saya gunakan:
sumber
Contoh sederhana:
sumber
Extension Array yang Berfungsi (bermutasi & tidak bermutasi)
Swift 4.1 / Xcode 9
Jawaban teratas sudah usang, jadi saya mengambil sendiri untuk membuat ekstensi sendiri untuk mengocok array dalam versi terbaru Swift, Swift 4.1 (Xcode 9):
Hubungi Non-Mutating Shuffle
[Array] -> [Array]
:Ini mencetak
array
dalam urutan acak.Panggil Mutating Shuffle
[Array] = [Array]
:Ini mencetak
array
dalam urutan saat ini, yang telah dikocok secara acak.Berharap ini bekerja untuk semua orang, jika Anda memiliki pertanyaan, saran, atau komentar, jangan ragu untuk bertanya!
sumber
Dalam SWIFT 4
sumber
Jika Anda ingin menggunakan fungsi Swift For loop yang sederhana gunakan ini ->
Array Swift suffle menggunakan ekstensi ->
sumber
Pada swift 4.2 ada dua fungsi praktis:
dan
sumber
Berikut beberapa kode yang berjalan di taman bermain. Anda tidak perlu mengimpor Darwin dalam proyek Xcode yang sebenarnya.
sumber
drand48()
memberikan angka acak semu yang sama setiap kali, kecuali jika Anda menetapkan seed dengan likesrand48(Int(arc4random()))
Itu berhenti di "swap (& self [i], & self [j])" ketika saya meningkatkan versi xCode ke 7.4 beta.
kesalahan fatal: menukar lokasi dengan dirinya sendiri tidak didukung
Jadi saya tambahkan syarat seperti di bawah ini
YA! Tidak masalah bagi saya.
sumber