Як перетасувати масив у Swift?


305

Як я рандомізую або переміщую елементи в масиві Swift? Наприклад, якщо мій масив складається з 52 гральних карт, я хочу перетасувати масив, щоб перемістити колоду.


2
це не характерно для будь-якої мови. Просто застосуйте будь-який алгоритм перетасовки ...
Габріеле Петронелла

8
@Mithrandir Це неправда. У Рубі можна було б поїхати array.shuffle. Не потрібно реалізовувати власну версію. Я думаю, що ОП шукала щось подібне.
Лінус Олеандр

1
будьте обережні, однак не використовуйте будь-який алгоритм перетасовки для переміщення колоди карт.
njzk2

Відповіді:


626

У цій відповіді детально описано, як переміщуватися за допомогою швидкого та рівномірного алгоритму (Fisher-Yates) у Swift 4.2+ та як додати ту саму особливість у різні попередні версії 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]

Швидкі версії 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.


Швидкий 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]

Швидкий 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]

1
Якщо ви хочете, щоб версія функції у Swift 1.2, вона потребує трохи оновлення, як countElementsце вже не було, і це заміна count, тепер повертає T.Index.Distanceтак, що обмеження потрібно ввімкнути C.Index.Distance == Int. Ця версія повинна працювати: gist.github.com/airspeedswift/03d07a9dc86fabdc370f
Швидкість

2
Це фактичний вихід - Fisher-Yates повинен повертати неупереджену випадкову перестановку джерела, тому немає необхідності, щоб певний елемент рухався. Там є гарантія того, що ні один елемент переміщається більш ніж один раз, але іноді «рух» є тим же індексом. Найпростіший випадок - подумати - чи повинні [1, 2].shuffled()це повертатися [2, 1]кожен раз?
Нейт Кук

1
Я додав if count > 0у верхній частині функції мутації масиву, щоб запобігти появі "фатальної помилки: Неможливо сформувати Діапазон із закінченням <початок", коли він передається порожнім масивом.
Карл Сміт

3
@Jan: Так, додайте 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протоколом - якщо ви додасте розширення до RNGs GameplayKit, щоб вони відповідали новому стандартному протоколу бібліотеки, ви можете легко отримати:

  • sendable 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()

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

5
імпорт GameplayKit просто для отримання перетасованого масиву не здається чудовою ідеєю
Lope

3
Чому? Це частина системи, не додає до двійкового.
Абізерн

3
Ви також можете скористатися імпортом простоimport GameplayKit.GKRandomSource
JRG-Developer

26

Ось дещо коротше:

sorted(a) {_, _ in arc4random() % 2 == 0}

1
@moby sortФункція потребує закриття для замовлення елементів. Це закриття має два параметри (elem1, elem2) і повинно повернути true, якщо перше значення повинне з’явитися перед другим значенням, а false - в іншому випадку. Якщо замість цього ми повернемо випадковий булев ... тоді ми просто змішаємо все це :)
Jean Le Moignan

2
Будь-який математик тут, щоб підтвердити чи спростувати?
Жан Ле Мойнан

9
Як вказував pjs у відповідь на іншу дуже подібну відповідь, це не призведе до рівномірного розподілу результатів. Використовуйте перемішання Фішера-Йейта, як показано у відповіді Нейт-Кука.
Роб

1
Це розумний трюк, але з точки зору якості перетасовки є ненормальним. Для одного, це закриття повинно використовуватись arc4random_uniform(), оскільки воно наразі піддається модульному зміщенню. По-друге, вихід дуже сильно залежить від алгоритму сортування (який нам не відомий, не дивлячись на джерело).
Олександр - Відновіть Моніку

1
Продовжуючи цей простіший підхід, це, здається, спрацює досить непогано: collection.sorted { _,_ in arc4random_uniform(1) == 0 }
markiv

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

У моєму випадку у мене виникли проблеми із заміною об’єктів у Array. Потім я почухав голову і пішов на винахід колеса.

// 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

Це версія виконання Нейтом перемикання Fisher-Yates для 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 ДодатиMutableCollection.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
}

3

Swift 4 Перемішайте елементи масиву в циклі for 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до вашого ВК та скористайтеся наведеним нижче кодом. Випробувано в 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примірника з відповідним елементом масиву.


Наступний код дитячої площадки показує, як це працює:

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"]

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
Принаймні, це страждає від серйозної помилки, описаної тут однією помилкою, завдяки якій значення завжди змінюється своєю вихідною позицією. Це усувається за допомогою . Крім того, у FY ви, як правило, повторюєте від нижнього до (або, якщо ви повторюєте, до , ви вибираєте індекс, як Нейт-шоу, у прийнятому відповіді). Дивіться розділ Сучасний алгоритм обговорення Фішера-Йейта. let rnd = Int(arc4random_uniform(UInt32(idx + 1)))arr.count - 110arr.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)")


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


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

Розширення робочого масиву (мутує і не мутує)

Швидкий 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у випадковому порядку.


Переміщення при виклику [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 Arle суфле, використовуючи розширення ->

extension Array {
    // Order Randomize
    mutating func shuffle() {
        for _ in 0..<count {
            sort { (_,_) in arc4random() < arc4random() }
        }
    }
}

0

Станом на швидкий 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

Ось код, який працює на дитячому майданчику. Вам не потрібно буде імпортувати Дарвін у фактичний проект 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) час.
пiс

Також drand48()щоразу дає ті самі псевдо випадкові числа, якщо ви не встановите насіння з подібнимиsrand48(Int(arc4random()))
Kametrixom

-3

Він зупиняється на "swap (& self [i], & self [j])", коли я оновлю версію xCode до бета-версії 7.4.
фатальна помилка: обмін місцезнаходженням із собою не підтримується

Я знайшов причину того, що i = j (функція swap вибухне)

Тому я додаю умову, як показано нижче

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

Я! Для мене це нормально.


Здається, це коментар до відповіді Кріса , а не відповідь на початкове запитання.
Могсдад
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.