Как перемешать массив в Swift?

305

Как мне рандомизировать или перемешать элементы в массиве в Swift? Например, если мой массив состоит из 52 игральных карт, я хочу перетасовать массив, чтобы перемешать колоду.

mpatzer
источник
2
это не относится ни к какому языку. Просто примените любой алгоритм перетасовки ...
Габриэле Петронелла
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()метод к любой последовательности:

extension MutableCollection {
    /// Shuffles the contents of this collection.
    mutating func shuffle() {
        let c = count
        guard c > 1 else { 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)
        }
    }
}

extension Sequence {
    /// 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()метод к любой последовательности:

extension MutableCollection where Indices.Iterator.Element == Index {
    /// Shuffles the contents of this collection.
    mutating func shuffle() {
        let c = count
        guard c > 1 else { 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 != 0 else { continue }
            let i = index(firstUnshuffled, offsetBy: d)
            self.swapAt(firstUnshuffled, i)
        }
    }
}

extension Sequence {
    /// 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 г.)

extension MutableCollectionType where Index == Int {
    /// Shuffle the elements of `self` in-place.
    mutating func 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])
        }
    }
}

extension CollectionType {
    /// 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экземпляр на месте:

extension Array {
    mutating func shuffle() {
        if count < 2 { return }
        for i in 0..<(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экземпляра:

extension Array {
    func shuffled() -> [T] {
        if count < 2 { return self }
        var list = self
        for i in 0..<(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]
Nate Cook
источник
1
Если вам нужна версия функции в 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 as NSArray).shuffled(using: random) // -> [Any]
let shuffled2 = (array as NSArray).shuffled() // use default random source

Но сделать обертку Swift для сохранения типов довольно просто:

extension Array {
    func shuffled(using source: GKRandomSource) -> [Element] {
        return (self as NSArray).shuffled(using: source) as! [Element]
    }
    func shuffled() -> [Element] {
        return (self as NSArray).shuffled() as! [Element]
    }
}
let shuffled3 = array.shuffled(using: random)
let shuffled4 = array.shuffled()
rickster
источник
6
Меня удивляет, какие еще полезные утилиты можно найти в GameplayKit, которые я никогда не изучал!
Ричард Венейбл
6
Поиск графиков, поиск по дереву, системы правил ... много всего полезного как в игровом дизайне, так и в других отношениях.
Рикстер
5
В Swift 3 / iOS 10 это было изменено на:let shuffled = lcg.arrayByShufflingObjects(in: array)
Эван Пон
30

В Swift 2.0 GameplayKit может прийти на помощь! (поддерживается iOS9 или новее)

import GameplayKit

func shuffle() {
    array = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(array)
}
bluenowhere
источник
5
импортирование GameplayKit просто для того, чтобы получить перемешанный массив, не
Лопе
3
Зачем? Это часть системы, не добавляет в двоичный файл.
Abizern
3
Вы также можете import GameplayKit.GKRandomSource
JRG-Developer
26

Вот что-то, возможно, немного короче:

sorted(a) {_, _ in arc4random() % 2 == 0}
Жан Ле Муаньян
источник
1
@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 и расширениями протокола.

Это то, что я придумал.

extension MutableCollectionType where Self.Index == Int {
    mutating func shuffleInPlace() {
        let c = self.count
        for i in 0..<(c - 1) {
            let j = Int(arc4random_uniform(UInt32(c - i))) + i
            swap(&self[i], &self[j])
        }
    }
}

extension MutableCollectionType where Self.Index == Int {
    func shuffle() -> Self {
        var r = self
        let c = self.count
        for i in 0..<(c - 1) {
            let j = Int(arc4random_uniform(UInt32(c - i))) + i
            swap(&r[i], &r[j])
        }
        return r
    }
}

Теперь любой MutableCollectionTypeможет использовать эти методы, если он использует их IntкакIndex

Крис Вагнер
источник
6

В моем случае у меня были некоторые проблемы с обменом объектов в массиве. Затем я почесал голову и начал изобретать велосипед.

// swift 3.0 ready
extension Array {

    func shuffled() -> [Element] {
        var results = [Element]()
        var indexes = (0 ..< count).map { $0 }
        while indexes.count > 0 {
            let indexOfIndexes = Int(arc4random_uniform(UInt32(indexes.count)))
            let index = indexes[indexOfIndexes]
            results.append(self[index])
            indexes.remove(at: indexOfIndexes)
        }
        return results
    }

}
Каз Йошикава
источник
5

Это версия реализации Нейтом шаффла Фишера-Йейтса для Swift 4 (Xcode 9).

extension MutableCollection {
    /// Shuffle the elements of `self` in-place.
    mutating func 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)
        }
    }
}

extension Collection {
    /// 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протокола и больше не нуждается в расширении.
  • Обмен элементов должен производиться путем вызова swapAt()коллекции, сравните SE-0173 AddMutableCollection.swapAt(_:_:) .
  • Elementэто псевдоним для Iterator.Element.
Мартин Р
источник
3

Это то, что я использую:

func newShuffledArray(array:NSArray) -> NSArray {
    var mutableArray = array.mutableCopy() as! NSMutableArray
    var count = mutableArray.count
    if count>1 {
        for var i=count-1;i>0;--i{
            mutableArray.exchangeObjectAtIndex(i, withObjectAtIndex: Int(arc4random_uniform(UInt32(i+1))))
        }
    }
    return mutableArray as NSArray
}
iliketopgun
источник
3

Swift 4 Перемешать элементы массива в цикле for, где i - коэффициент смешивания

var cards = [Int]() //Some Array
let i = 4 // is the mixing ratio
func shuffleCards() {
    for _ in 0 ..< 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 _ in 0 ..< cards.count * i {
        let card = cards.remove(at: cards.count.arc4random)
        cards.insert(card, at: cards.count.arc4random)
    }
}
extension Int {
    var arc4random: Int {
        if self > 0 {
            print("Arc for random positiv self \(Int(arc4random_uniform(UInt32(self))))")
        return Int(arc4random_uniform(UInt32(self)))
        } else if self < 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")
            return 0
        }
    }
}
Сергей
источник
2

Решение Swift 3, после ответа @Nate Cook: (работает, если индекс начинается с 0, см. Комментарии ниже)

extension Collection {
    /// Return a copy of `self` with its elements shuffled
    func shuffle() -> [Generator.Element] {
        var list = Array(self)
        list.shuffleInPlace()
        return list
    } }

extension MutableCollection where Index == Int {
    /// Shuffle the elements of `self` in-place.
    mutating func shuffleInPlace() {
        // empty and single-element collections don't shuffle
        if count < 2 { return }
        let countInt = count as! Int

    for i in 0..<countInt - 1 {
        let j = Int(arc4random_uniform(UInt32(countInt - i))) + i
            guard i != j else { continue }
            swap(&self[i], &self[j])
        }
    }
}
Ансон Яо
источник
1
Это может привести к сбою, если индексы коллекции начинаются с 0, например, для среза массива. Попробуйте запустить var a = [1, 2, 3, 4, 5, 6][3..<6]; a.shuffleInPlace()несколько раз. - См. Stackoverflow.com/a/37843901/1187415 для правильного решения.
Мартин Р
2

Вот как это делается самым простым способом. import Gamplaykitк вашему VC и используйте приведенный ниже код. Проверено в Xcode 8.

 import GameplayKit

 let array: NSArray = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]

 override func viewDidLoad() {
    super.viewDidLoad()

    print(array.shuffled())  
}

Если вы хотите получить перемешанную строку из массива, вы можете использовать следующий код:

func suffleString() {

    let ShuffleArray = array.shuffled()

    suffleString.text = ShuffleArray.first as? String

    print(suffleString.text!)

}
Джо
источник
2

С Swift 3, если вы хотите перетасовать массив на месте или получить новый перетасованный массив из массива, AnyIteratorвам могут помочь. Идея состоит в том, чтобы создать массив индексов из вашего массива, перетасовать эти индексы с помощью AnyIteratorэкземпляра и swap(_:_:)функции и отобразить каждый элемент этого AnyIteratorэкземпляра с соответствующим элементом массива.


Следующий код Playground показывает, как это работает:

import Darwin // 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 {
    guard let nextIndex = indexArray.index(index, offsetBy: -1, limitedBy: indexArray.startIndex)
        else { return nil }

    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расширения, чтобы получить новый перетасованный массив из массива:

import Darwin // required for arc4random_uniform

extension Array {

    func shuffled() -> Array<Element> {
        var indexArray = Array<Int>(indices)        
        var index = indexArray.endIndex

        let indexIterator = AnyIterator<Int> {
            guard let nextIndex = indexArray.index(index, offsetBy: -1, limitedBy: indexArray.startIndex)
                else { return nil }

            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расширения, чтобы перетасовать массив на месте:

import Darwin // required for arc4random_uniform

extension Array {

    mutating func shuffle() {
        var indexArray = Array<Int>(indices)
        var index = indexArray.endIndex

        let indexIterator = AnyIterator<Int> {
            guard let nextIndex = indexArray.index(index, offsetBy: -1, limitedBy: indexArray.startIndex)
                else { return nil }

            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"]
Imanou Petit
источник
1

Вы также можете использовать универсальную swapфункцию и реализовать упомянутые Fisher-Yates:

for idx in 0..<arr.count {
  let rnd = Int(arc4random_uniform(UInt32(idx)))
  if rnd != idx {
    swap(&arr[idx], &arr[rnd])
  }
}

или менее многословный:

for idx in 0..<steps.count {
  swap(&steps[idx], &steps[Int(arc4random_uniform(UInt32(idx)))])
}
Даниэль Бауке
источник
2
Это страдает, по крайней мере, от серьезного отключения одной ошибкой, описанной здесь, в результате чего значение всегда меняется с исходного положения. Это исправлено let rnd = Int(arc4random_uniform(UInt32(idx + 1))). Кроме того, в FY вы обычно выполняете итерацию от arr.count - 1вниз до 1(или, если вы переходите от 0к arr.count - 1, вы выбираете индекс, как показано Нейтом в принятом ответе). См. Раздел « Современный алгоритм» в обсуждении Фишера-Йейтса.
Роб
1

работает!!. организмы - это массив для перемешивания.

extension Array
{
    /** Randomizes the order of an array's elements. */
    mutating func shuffle()
    {
        for _ in 0..<10
        {
            sort { (_,_) in arc4random() < arc4random() }
        }
    }
}

var organisms = [
    "ant",  "bacteria", "cougar",
    "dog",  "elephant", "firefly",
    "goat", "hedgehog", "iguana"]

print("Original: \(organisms)")

organisms.shuffle()

print("Shuffled: \(organisms)")
Vimal
источник
0

Это как перемешать один массив с начальным числом в Swift 3.0.

extension MutableCollection where Indices.Iterator.Element == Index {
    mutating func shuffle() {
        let c = count
        guard c > 1 else { return }


        for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
            srand48(seedNumber)
            let number:Int = numericCast(unshuffledCount)
            let r = floor(drand48() * Double(number))

            let d: IndexDistance = numericCast(Int(r))
            guard d != 0 else { continue }
            let i = index(firstUnshuffled, offsetBy: d)
            swap(&self[firstUnshuffled], &self[i])
        }
    }
}
Tayo119
источник
0
let shuffl = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: arrayObject)
Рохит Сисодиа
источник
0

Это то, что я использую:

import GameplayKit

extension Collection {
    func shuffled() -> [Iterator.Element] {
        let shuffledArray = (self as? NSArray)?.shuffled()
        let outputArray = shuffledArray as? [Iterator.Element]
        return outputArray ?? []
    }
    mutating func shuffle() {
        if let selfShuffled = self.shuffled() as? Self {
            self = selfShuffled
        }
    }
}

// Usage example:

var numbers = [1,2,3,4,5]
numbers.shuffle()

print(numbers) // output example: [2, 3, 5, 4, 1]

print([10, "hi", 9.0].shuffled()) // output example: [hi, 10, 9]
Даниэль Иллескас
источник
0

Простой пример:

extension Array {
    mutating func shuffled() {
        for _ in self {
            // 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]
Бобби
источник
0

Расширение рабочего массива (мутирующее и не мутирующее)

Swift 4.1 / Xcode 9

Верхний ответ устарел, поэтому я решил создать собственное расширение для перемешивания массива в новейшей версии Swift, Swift 4.1 (Xcode 9):

extension Array {

// Non-mutating shuffle
    var shuffled : Array {
        let totalCount : Int = self.count
        var shuffledArray : Array = []
        var count : Int = totalCount
        var tempArray : Array = self
        for _ in 0..<totalCount {
            let randomIndex : Int = Int(arc4random_uniform(UInt32(count)))
            let randomElement : Element = tempArray.remove(at: randomIndex)
            shuffledArray.append(randomElement)
            count -= 1
        }
        return shuffledArray
    }

// Mutating shuffle
    mutating func shuffle() {
        let totalCount : Int = self.count
        var shuffledArray : Array = []
        var count : Int = totalCount
        var tempArray : Array = self
        for _ in 0..<totalCount {
            let randomIndex : Int = Int(arc4random_uniform(UInt32(count)))
            let randomElement : Element = tempArray.remove(at: randomIndex)
            shuffledArray.append(randomElement)
            count -= 1
        }
        self = shuffledArray
    }
}

Вызвать негласное перемешивание [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)

Это печатает 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в его текущем порядке, который уже был случайно перемешан.


Надеемся, что это работает для всех, если у вас есть какие-либо вопросы, предложения или комментарии, не стесняйтесь спрашивать!

Ной Уайлдер
источник
0

В SWIFT 4

func createShuffledSequenceOfNumbers(max:UInt)->[UInt] {

    var array:[UInt]! = []
    var myArray:[UInt]! = []
    for i in 1...max {
        myArray.append(i)
    }
    for i in 1...max {
        array.append(i)
    }
    var tempArray:[Int]! = []
    for index in 0...(myArray.count - 1) {

        var isNotFinded:Bool = true
        while(isNotFinded){

            let randomNumber = arc4random_uniform(UInt32(myArray.count))
            let randomIndex = Int(randomNumber)

            if(!tempArray.contains(randomIndex)){
                tempArray.append(randomIndex)

                array[randomIndex] = myArray[index]
                isNotFinded = false
            }
        }
    }

    return array
}
Али Хезри
источник
0

Если вы хотите использовать простую функцию цикла Swift For, используйте это ->

var arrayItems = ["A1", "B2", "C3", "D4", "E5", "F6", "G7", "H8", "X9", "Y10", "Z11"]
var shuffledArray = [String]()

for i in 0..<arrayItems.count
{
    let randomObject = Int(arc4random_uniform(UInt32(items.count)))

    shuffledArray.append(items[randomObject])

    items.remove(at: randomObject)
}

print(shuffledArray)

Swift Array суфле используя расширение ->

extension Array {
    // Order Randomize
    mutating func shuffle() {
        for _ in 0..<count {
            sort { (_,_) in arc4random() < arc4random() }
        }
    }
}
Рахул Сингха Рой
источник
0

Начиная с Swift 4.2 есть две удобные функции:

// shuffles the array in place
myArray.shuffle()

и

// generates a new array with shuffled elements of the old array
let newArray = myArray.shuffled()
быстрая рысь
источник
-2

Вот код, который работает на детской площадке. Вам не нужно импортировать Darwin в настоящий проект Xcode.

import darwin

var a = [1,2,3,4,5,6,7]

func shuffle<ItemType>(item1: ItemType, item2: ItemType) -> Bool {
    return drand48() > 0.5
}

sort(a, shuffle)

println(a)
Дэн Хиксон
источник
7
Это дает неравномерное распределение результатов. Это также будет O (n log n), где тасование Фишера-Йейтса даст равномерно распределенные результаты за O (n) время.
pjs
Также drand48()каждый раз выдает одни и те же псевдослучайные числа, если только вы не установили начальное число с подобнымsrand48(Int(arc4random()))
Kametrixom
-3

Он останавливается на «swap (& self [i], & self [j])» при обновлении версии xCode до бета-версии 7.4.
фатальная ошибка: обмен местоположением с самим собой не поддерживается

Я нашел причину, по которой i = j (функция свопа взорвалась)

Поэтому я добавляю условие, как показано ниже

if (i != j){
    swap(&list[i], &list[j])
}

YA! Это хорошо для меня.

米米米
источник
Похоже, это комментарий к ответу Криса , а не ответ на первоначальный вопрос.
Могсдад