Shuffle array swift 3

Столкнувшись с этим недавно на XCode 7.3.1 - для меня, я заметил, что использование ОЗУ будет 100% на CleanMyMac3. Проблема волшебным образом закрепилась после перезагрузки моей машины. Справедливости ради, однако, я уже пошел вперед и попробовал принятый ответ, так что вы захотите сделать то же самое, прежде чем перезапустить на всякий случай: -)

29
задан Community 23 May 2017 в 11:47
поделиться

4 ответа

count возвращает IndexDistance, который является типом, описывающим расстояние между двумя индексами сбора. IndexDistance должен быть SignedInteger, но не обязательно должен быть Int и может отличаться от Index. Поэтому невозможно создать диапазон 0..<count - 1.

Решение состоит в том, чтобы использовать startIndex и endIndex вместо 0 и count:

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

        for i in startIndex ..< endIndex - 1 {
            let j = Int(arc4random_uniform(UInt32(endIndex - i))) + i
            if i != j {
                swap(&self[i], &self[j])
            }
        }
    }
}

Другим преимуществом является то, что это также работает правильно со срезами массива ] (где индекс первого элемента не обязательно равен нулю).

Обратите внимание, что в соответствии с новым «Руководством по проектированию Swift API» , shuffle() является «правильным» именем для метода с мутировавшим перемешиванием, а shuffled() для немутирующего аналога, который возвращает array:

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

Обновление: A (еще более общая) версия Swift 3 была добавлена ​​в Как мне переставить массив в Swift? тем временем .


Для Swift 4 (Xcode 9) необходимо заменить вызов функции swap() на вызов метода swapAt() коллекции. Также больше не нужно ограничение на тип Index:

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)
        }
    }
}

См. SE-0173 Добавить MutableCollection.swapAt(_:_:) для получения дополнительной информации о swapAt.


Начиная с Swift 4.2 (Xcode 10, в настоящее время находится в бета-версии), с реализацией SE-0202 Random Unification , shuffle() и shuffled() являются частью стандартной библиотеки Swift.

78
ответ дан Martin R 23 May 2017 в 11:47
поделиться

Я бы предложил просто перетасовать массивы вместо того, чтобы пытаться распространить это на коллекции в целом:

extension Array {
    mutating func shuffle () {
        for i in (0..<self.count).reversed() {
            let ix1 = i
            let ix2 = Int(arc4random_uniform(UInt32(i+1)))
            (self[ix1], self[ix2]) = (self[ix2], self[ix1])
        }
    }
}
8
ответ дан matt 23 May 2017 в 11:47
поделиться

В Gamekit есть тасовка Фишера-Йейтса:

import GameKit
let unshuffledArray = [1,2,3,4]
let shuffledArray = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: unshuffledArray)
print(shuffledArray)

Вы также можете передать и сохранить случайное начальное число, так что вы получаете одинаковую последовательность значений псевдослучайного тасования при каждом подаче одного и того же начального числа. на случай, если вам понадобится воссоздать симуляцию.

import GameKit
let unshuffledArray = [1,2,3,4]
let randomSource = GKLinearCongruentialRandomSource(seed: 1)
let shuffledArray = randomSource.arrayByShufflingObjects(in: unshuffledArray)
//Always [1,4,2,3]
print(shuffledArray)
10
ответ дан Josh Homann 23 May 2017 в 11:47
поделиться

Для этого вы можете использовать расширение NSArray из каркаса GameplayKit:

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
ответ дан Daniel Illescas 23 May 2017 в 11:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: