Как мне рандомизировать или перемешать элементы в массиве в Swift? Например, если мой массив состоит из 52 игральных карт, я хочу перетасовать массив, чтобы перемешать колоду.
это не относится ни к какому языку. Просто примените любой алгоритм перетасовки ...
Габриэле Петронелла
8
@Mithrandir Это не правда. В Ruby можно было бы пойти array.shuffle. Нет необходимости реализовывать собственную версию. Я думаю, что OP искал что-то подобное.
Линус Олеандер
1
будьте осторожны, однако, не используйте какой-либо алгоритм перемешивания, чтобы перемешать колоду карт.
njzk2
Ответы:
627
В этом ответе подробно рассказывается, как перемешать с помощью быстрого и унифицированного алгоритма (Фишера-Йейтса) в Swift 4.2+ и как добавить эту же функцию в различные предыдущие версии Swift. Именование и поведение для каждой версии Swift соответствуют методам сортировки с изменением и изменением для этой версии.
Swift 4.2+
shuffleи shuffledродные начиная с Swift 4.2. Пример использования:
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 и 4.1
Эти расширения добавляют shuffle()метод к любой изменяемой коллекции (массивы и небезопасные изменяемые буферы) и shuffled()метод к любой последовательности:
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
}}
То же использование, что и в приведенных выше примерах Swift 4.2.
Свифт 3
Эти расширения добавляют shuffle()метод к любой изменяемой коллекции и shuffled()метод к любой последовательности:
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
}}
То же использование, что и в приведенных выше примерах Swift 4.2.
Swift 2
(устаревший язык: вы не можете использовать Swift 2.x для публикации в iTunes Connect начиная с июля 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
}}
Использование:
[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
(устаревший язык: вы не можете использовать Swift 1.x для публикации в iTunes Connect начиная с июля 2018 года)
shuffle как метод мутирующего массива
Это расширение позволит вам перемешать изменяемый Arrayэкземпляр на месте:
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 как метод не мутирующего массива
Это расширение позволит вам получить перемешанную копию Arrayэкземпляра:
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]
Если вам нужна версия функции в Swift 1.2, она нуждается в небольшом обновлении по мере того, как countElementsпрошло, и его замена countтеперь возвращает значение, T.Index.Distanceпоэтому ограничение должно быть включено C.Index.Distance == Int. Эта версия должна работать: gist.github.com/airspeedswift/03d07a9dc86fabdc370f
Скорость
2
Это фактический результат - Фишер-Йейтс должен возвращать непредвзятую случайную перестановку источника, поэтому не требуется, чтобы конкретный элемент перемещался. Там есть гарантия того, что ни один элемент перемещается более чем один раз, но иногда «движение» является тем же индексом. Простейший случай - подумать - должно [1, 2].shuffled()ли это возвращаться [2, 1]каждый раз?
Нейт Кук
1
Я добавил if count > 0в начало функции мутирующего массива, чтобы предотвратить получение «фатальной ошибки: невозможно сформировать Range с end <start», когда ему передается пустой массив.
Карл Смит
3
@ Ян: Да, добавить guard i != j else { continue }до обмена. Я подал радар, но новое поведение является преднамеренным.
Нейт Кук
3
На самом деле shuffleInPlaceможет произойти сбой, если индексы коллекции не начинаются с нуля, например, для среза массива. for i in 0..<count - 1 должно быть for i in startIndex ..< endIndex - 1(и тогда преобразование в Swift 3 становится почти тривиальным).
Мартин Р
131
Редактировать: Как отмечалось в других ответах, Swift 4.2 наконец добавляет генерацию случайных чисел в стандартную библиотеку, в комплекте с перестановкой массива.
Тем не менее, GKRandom/ GKRandomDistributionsuite в GameplayKit все еще может быть полезен с новым RandomNumberGeneratorпротоколом - если вы добавите расширения в RNG GameplayKit для соответствия новому стандартному протоколу библиотеки, вы можете легко получить:
отправляемые RNG (которые могут воспроизводить «случайную» последовательность при необходимости для тестирования)
RNG, которые жертвуют надежностью ради скорости
ГСЧ, которые дают неравномерное распределение
... и по-прежнему использовать новые приятные "родные" случайные API в Swift.
Остальная часть этого ответа касается таких RNG и / или их использования в более старых компиляторах Swift.
Здесь уже есть несколько хороших ответов, а также несколько хороших иллюстраций того, почему написание собственного шаффла может быть подвержено ошибкам, если вы не будете осторожны.
В iOS 9, macOS 10.11 и tvOS 9 (или более поздних версиях) вам не нужно писать свои собственные. В GameplayKit есть эффективная, правильная реализация Fisher-Yates (которая, несмотря на название, предназначена не только для игр).
Если вы просто хотите уникальное перемешивание:
let shuffled =GKRandomSource.sharedRandom().arrayByShufflingObjects(in: array)
Если вы хотите быть в состоянии воспроизвести случайное или последовательное воспроизведение, выберите и запустите конкретный случайный источник; например
let lcg =GKLinearCongruentialRandomSource(seed: mySeedValue)let shuffled = lcg.arrayByShufflingObjects(in: array)
В iOS 10 / macOS 10.12 / tvOS 10 также есть удобный синтаксис для перетасовки через расширение NSArray. Конечно, это немного громоздко, когда вы используете Swift Array(и он теряет свой тип элемента при возвращении в Swift):
let shuffled1=(array asNSArray).shuffled(using: random)// -> [Any]
let shuffled2=(array asNSArray).shuffled()// use default random source
Но сделать обертку Swift для сохранения типов довольно просто:
@moby sortФункция нуждается в закрытии для упорядочивания элементов. Это закрытие принимает два параметра (elem1, elem2) и должно возвращать true, если первое значение должно стоять перед вторым значением, и false в противном случае. Если мы вместо этого возвращаем случайное логическое значение ... тогда мы просто все перепутаем :)
Жан Ле Мойнан
2
Любой математик здесь, чтобы подтвердить или опровергнуть?
Жан Ле Муаньян
9
Как указал pjs в ответ на другой очень похожий ответ, это не приведет к равномерному распределению результатов. Используйте Fisher-Yates Shuffle, как показано в ответе Нейта Кука.
Роб
1
Это хитрый трюк, но он ужасен с точки зрения качества перемешивания. Во-первых, это закрытие следует использовать arc4random_uniform(), потому что в настоящее время оно подвержено смещению по модулю. Во-вторых, вывод очень сильно зависит от алгоритма сортировки (который нам неизвестен, не глядя на источник).
Александр - Восстановить Монику
1
Продолжая этот более простой подход, это, кажется, работает довольно хорошо: collection.sorted { _,_ in arc4random_uniform(1) == 0 }
Маркив
7
Взяв алгоритм Нейта, я хотел посмотреть, как это будет выглядеть с Swift 2 и расширениями протокола.
Это то, что я придумал.
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
}}
Теперь любой MutableCollectionTypeможет использовать эти методы, если он использует их IntкакIndex
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
}}
Изменения:
Ограничение Indices.Iterator.Element == Indexтеперь является частью Collectionпротокола и больше не нуждается в расширении.
Swift 4
Перемешать элементы массива в цикле for, где i - коэффициент смешивания
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))))}}
Или с расширением 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, после ответа @Nate Cook: (работает, если индекс начинается с 0, см. Комментарии ниже)
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])}}}
Это может привести к сбою, если индексы коллекции начинаются с 0, например, для среза массива. Попробуйте запустить var a = [1, 2, 3, 4, 5, 6][3..<6]; a.shuffleInPlace()несколько раз. - См. Stackoverflow.com/a/37843901/1187415 для правильного решения.
Мартин Р
2
Вот как это делается самым простым способом. import Gamplaykitк вашему VC и используйте приведенный ниже код. Проверено в Xcode 8.
С Swift 3, если вы хотите перетасовать массив на месте или получить новый перетасованный массив из массива, AnyIteratorвам могут помочь. Идея состоит в том, чтобы создать массив индексов из вашего массива, перетасовать эти индексы с помощью AnyIteratorэкземпляра и swap(_:_:)функции и отобразить каждый элемент этого AnyIteratorэкземпляра с соответствующим элементом массива.
Следующий код Playground показывает, как это работает:
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"]
Вы можете выполнить рефакторинг предыдущего кода и создать shuffled()функцию внутри 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]}}}
Использование:
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:[]
В качестве альтернативы предыдущему коду вы можете создать shuffle() функцию внутри Arrayрасширения, чтобы перетасовать массив на месте:
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]}}}
Использование:
var mutatingArray =["Jock","Ellie","Sue Ellen","Bobby","JR","Pamela"]
mutatingArray.shuffle()
print(mutatingArray)// may print ["Sue Ellen","Pamela","Jock","Ellie","Bobby","JR"]
Это страдает, по крайней мере, от серьезного отключения одной ошибкой, описанной здесь, в результате чего значение всегда меняется с исходного положения. Это исправлено let rnd = Int(arc4random_uniform(UInt32(idx + 1))). Кроме того, в FY вы обычно выполняете итерацию от arr.count - 1вниз до 1(или, если вы переходите от 0к arr.count - 1, вы выбираете индекс, как показано Нейтом в принятом ответе). См. Раздел « Современный алгоритм» в обсуждении Фишера-Йейтса.
Роб
1
работает!!. организмы - это массив для перемешивания.
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]
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)
Это печатает arrayв случайном порядке.
Call 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)
Это печатает arrayв его текущем порядке, который уже был случайно перемешан.
Надеемся, что это работает для всех, если у вас есть какие-либо вопросы, предложения или комментарии, не стесняйтесь спрашивать!
Это дает неравномерное распределение результатов. Это также будет O (n log n), где тасование Фишера-Йейтса даст равномерно распределенные результаты за O (n) время.
pjs
Также drand48()каждый раз выдает одни и те же псевдослучайные числа, если только вы не установили начальное число с подобнымsrand48(Int(arc4random()))
Kametrixom
-3
Он останавливается на «swap (& self [i], & self [j])» при обновлении версии xCode до бета-версии 7.4.
фатальная ошибка: обмен местоположением с самим собой не поддерживается
Я нашел причину, по которой i = j (функция свопа взорвалась)
array.shuffle
. Нет необходимости реализовывать собственную версию. Я думаю, что OP искал что-то подобное.Ответы:
В этом ответе подробно рассказывается, как перемешать с помощью быстрого и унифицированного алгоритма (Фишера-Йейтса) в Swift 4.2+ и как добавить эту же функцию в различные предыдущие версии Swift. Именование и поведение для каждой версии Swift соответствуют методам сортировки с изменением и изменением для этой версии.
Swift 4.2+
shuffle
иshuffled
родные начиная с Swift 4.2. Пример использования:Swift 4.0 и 4.1
Эти расширения добавляют
shuffle()
метод к любой изменяемой коллекции (массивы и небезопасные изменяемые буферы) иshuffled()
метод к любой последовательности:То же использование, что и в приведенных выше примерах Swift 4.2.
Свифт 3
Эти расширения добавляют
shuffle()
метод к любой изменяемой коллекции иshuffled()
метод к любой последовательности:То же использование, что и в приведенных выше примерах Swift 4.2.
Swift 2
(устаревший язык: вы не можете использовать Swift 2.x для публикации в iTunes Connect начиная с июля 2018 г.)
Использование:
Swift 1.2
(устаревший язык: вы не можете использовать Swift 1.x для публикации в iTunes Connect начиная с июля 2018 года)
shuffle
как метод мутирующего массиваЭто расширение позволит вам перемешать изменяемый
Array
экземпляр на месте:shuffled
как метод не мутирующего массиваЭто расширение позволит вам получить перемешанную копию
Array
экземпляра:источник
countElements
прошло, и его заменаcount
теперь возвращает значение,T.Index.Distance
поэтому ограничение должно быть включеноC.Index.Distance == Int
. Эта версия должна работать: gist.github.com/airspeedswift/03d07a9dc86fabdc370f[1, 2].shuffled()
ли это возвращаться[2, 1]
каждый раз?if count > 0
в начало функции мутирующего массива, чтобы предотвратить получение «фатальной ошибки: невозможно сформировать Range с end <start», когда ему передается пустой массив.guard i != j else { continue }
до обмена. Я подал радар, но новое поведение является преднамеренным.shuffleInPlace
может произойти сбой, если индексы коллекции не начинаются с нуля, например, для среза массива.for i in 0..<count - 1
должно бытьfor i in startIndex ..< endIndex - 1
(и тогда преобразование в Swift 3 становится почти тривиальным).Редактировать: Как отмечалось в других ответах, Swift 4.2 наконец добавляет генерацию случайных чисел в стандартную библиотеку, в комплекте с перестановкой массива.
Тем не менее,
GKRandom
/GKRandomDistribution
suite в GameplayKit все еще может быть полезен с новымRandomNumberGenerator
протоколом - если вы добавите расширения в RNG GameplayKit для соответствия новому стандартному протоколу библиотеки, вы можете легко получить:... и по-прежнему использовать новые приятные "родные" случайные API в Swift.
Остальная часть этого ответа касается таких RNG и / или их использования в более старых компиляторах Swift.
Здесь уже есть несколько хороших ответов, а также несколько хороших иллюстраций того, почему написание собственного шаффла может быть подвержено ошибкам, если вы не будете осторожны.
В iOS 9, macOS 10.11 и tvOS 9 (или более поздних версиях) вам не нужно писать свои собственные. В GameplayKit есть эффективная, правильная реализация Fisher-Yates (которая, несмотря на название, предназначена не только для игр).
Если вы просто хотите уникальное перемешивание:
Если вы хотите быть в состоянии воспроизвести случайное или последовательное воспроизведение, выберите и запустите конкретный случайный источник; например
В iOS 10 / macOS 10.12 / tvOS 10 также есть удобный синтаксис для перетасовки через расширение
NSArray
. Конечно, это немного громоздко, когда вы используете SwiftArray
(и он теряет свой тип элемента при возвращении в Swift):Но сделать обертку Swift для сохранения типов довольно просто:
источник
let shuffled = lcg.arrayByShufflingObjects(in: array)
В Swift 2.0 GameplayKit может прийти на помощь! (поддерживается iOS9 или новее)
источник
import GameplayKit.GKRandomSource
Вот что-то, возможно, немного короче:
источник
sort
Функция нуждается в закрытии для упорядочивания элементов. Это закрытие принимает два параметра (elem1, elem2) и должно возвращать true, если первое значение должно стоять перед вторым значением, и false в противном случае. Если мы вместо этого возвращаем случайное логическое значение ... тогда мы просто все перепутаем :)arc4random_uniform()
, потому что в настоящее время оно подвержено смещению по модулю. Во-вторых, вывод очень сильно зависит от алгоритма сортировки (который нам неизвестен, не глядя на источник).collection.sorted { _,_ in arc4random_uniform(1) == 0 }
Взяв алгоритм Нейта, я хотел посмотреть, как это будет выглядеть с Swift 2 и расширениями протокола.
Это то, что я придумал.
Теперь любой
MutableCollectionType
может использовать эти методы, если он использует ихInt
какIndex
источник
В моем случае у меня были некоторые проблемы с обменом объектов в массиве. Затем я почесал голову и начал изобретать велосипед.
источник
Это версия реализации Нейтом шаффла Фишера-Йейтса для Swift 4 (Xcode 9).
Изменения:
Indices.Iterator.Element == Index
теперь является частьюCollection
протокола и больше не нуждается в расширении.swapAt()
коллекции, сравните SE-0173 AddMutableCollection.swapAt(_:_:)
.Element
это псевдоним дляIterator.Element
.источник
Это то, что я использую:
источник
Swift 4 Перемешать элементы массива в цикле for, где i - коэффициент смешивания
Или с расширением Int
источник
Решение Swift 3, после ответа @Nate Cook: (работает, если индекс начинается с 0, см. Комментарии ниже)
источник
var a = [1, 2, 3, 4, 5, 6][3..<6]; a.shuffleInPlace()
несколько раз. - См. Stackoverflow.com/a/37843901/1187415 для правильного решения.Вот как это делается самым простым способом.
import Gamplaykit
к вашему VC и используйте приведенный ниже код. Проверено в Xcode 8.Если вы хотите получить перемешанную строку из массива, вы можете использовать следующий код:
источник
С Swift 3, если вы хотите перетасовать массив на месте или получить новый перетасованный массив из массива,
AnyIterator
вам могут помочь. Идея состоит в том, чтобы создать массив индексов из вашего массива, перетасовать эти индексы с помощьюAnyIterator
экземпляра иswap(_:_:)
функции и отобразить каждый элемент этогоAnyIterator
экземпляра с соответствующим элементом массива.Следующий код Playground показывает, как это работает:
Вы можете выполнить рефакторинг предыдущего кода и создать
shuffled()
функцию внутриArray
расширения, чтобы получить новый перетасованный массив из массива:Использование:
В качестве альтернативы предыдущему коду вы можете создать
shuffle()
функцию внутриArray
расширения, чтобы перетасовать массив на месте:Использование:
источник
Вы также можете использовать универсальную
swap
функцию и реализовать упомянутые Fisher-Yates:или менее многословный:
источник
let rnd = Int(arc4random_uniform(UInt32(idx + 1)))
. Кроме того, в FY вы обычно выполняете итерацию отarr.count - 1
вниз до1
(или, если вы переходите от0
кarr.count - 1
, вы выбираете индекс, как показано Нейтом в принятом ответе). См. Раздел « Современный алгоритм» в обсуждении Фишера-Йейтса.работает!!. организмы - это массив для перемешивания.
источник
В Swift 4.2 теперь есть метод как изменяемого, так
shuffle
и неизменяемогоshuffled
. Вы можете прочитать больше о случайной генерации и массивах здесь .источник
Это как перемешать один массив с начальным числом в Swift 3.0.
источник
источник
Это то, что я использую:
источник
Простой пример:
источник
Расширение рабочего массива (мутирующее и не мутирующее)
Swift 4.1 / Xcode 9
Верхний ответ устарел, поэтому я решил создать собственное расширение для перемешивания массива в новейшей версии Swift, Swift 4.1 (Xcode 9):
Вызвать негласное перемешивание
[Array] -> [Array]
:Это печатает
array
в случайном порядке.Call Mutating Shuffle
[Array] = [Array]
:Это печатает
array
в его текущем порядке, который уже был случайно перемешан.Надеемся, что это работает для всех, если у вас есть какие-либо вопросы, предложения или комментарии, не стесняйтесь спрашивать!
источник
В SWIFT 4
источник
Если вы хотите использовать простую функцию цикла Swift For, используйте это ->
Swift Array суфле используя расширение ->
источник
Начиная с Swift 4.2 есть две удобные функции:
и
источник
Вот код, который работает на детской площадке. Вам не нужно импортировать Darwin в настоящий проект Xcode.
источник
drand48()
каждый раз выдает одни и те же псевдослучайные числа, если только вы не установили начальное число с подобнымsrand48(Int(arc4random()))
Он останавливается на «swap (& self [i], & self [j])» при обновлении версии xCode до бета-версии 7.4.
фатальная ошибка: обмен местоположением с самим собой не поддерживается
Поэтому я добавляю условие, как показано ниже
YA! Это хорошо для меня.
источник