Jak przetasować tablicę w Swift?

305

Jak losowo lub tasować elementy w tablicy w Swift? Na przykład, jeśli moja tablica składa się z 52 kart do gry, chcę przetasować tablicę, aby przetasować talię.

mpatzer
źródło
2
nie jest to specyficzne dla żadnego języka. Po prostu zastosuj dowolny algorytm tasowania ...
Gabriele Petronella
8
@Mithrandir To nieprawda. W Ruby można by to zrobić array.shuffle. Nie ma potrzeby implementowania własnej wersji. Chyba OP szukał czegoś podobnego.
Linus Oleander
1
uważaj jednak, nie używaj żadnego algorytmu losowego do tasowania talii kart.
njzk2

Odpowiedzi:

626

Ta odpowiedź szczegółowo opisuje, jak tasować za pomocą szybkiego i jednolitego algorytmu (Fisher-Yates) w Swift 4.2+ oraz jak dodać tę samą funkcję w różnych poprzednich wersjach Swift. Nazwy i zachowanie każdej wersji Swift są zgodne z mutującymi i niemutującymi metodami sortowania dla tej wersji.

Swift 4.2+

shufflei shuffledsą rodzime od Swift 4.2. Przykładowe użycie:

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 i 4.1

Te rozszerzenia dodają shuffle()metodę do dowolnej kolekcji podlegającej mutacji (tablice i niebezpieczne bufory zmienne) oraz shuffled()metodę do dowolnej sekwencji:

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

Takie samo użycie jak w powyższych przykładach Swift 4.2.


Szybki 3

Te rozszerzenia dodają shuffle()metodę do dowolnej kolekcji mutowalnej i shuffled()metodę do dowolnej sekwencji:

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

Takie samo użycie jak w powyższych przykładach Swift 4.2.


Swift 2

(przestarzały język: nie można używać Swift 2.x do publikowania w iTunes Connect od lipca 2018 r.)

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

Stosowanie:

[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

(przestarzały język: nie można używać Swift 1.x do publikowania w iTunes Connect od lipca 2018 r.)

shuffle jako metoda tablicy mutującej

To rozszerzenie pozwoli ci przetasować zmienną Arrayinstancję w miejscu:

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 jako niemodyfikująca metoda tablicowa

To rozszerzenie pozwala odzyskać przetasowaną kopię Arrayinstancji:

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
źródło
1
Jeśli chcesz mieć wersję funkcji w Swift 1.2, wymaga ona trochę aktualizacji po jej countElementszniknięciu, a jej zastąpienie countzwraca teraz, T.Index.Distancewięc ograniczenie musi być włączone C.Index.Distance == Int. Ta wersja powinna działać: gist.github.com/airspeedswift/03d07a9dc86fabdc370f
Prędkość
2
To są rzeczywiste dane wyjściowe - Fisher-Yates powinien zwrócić obiektywną permutację źródła, więc nie ma wymogu, aby jakiś element się poruszał. Jest to gwarancja, że żaden element porusza się więcej niż raz, ale czasami „ruch” jest tym samym indeksie. Najprostszym przypadkiem jest zastanowienie się [1, 2].shuffled()- czy powinien wrócić za [2, 1]każdym razem?
Nate Cook
1
Dodałem if count > 0na górze funkcji mutującej tablicy, aby zapobiec otrzymaniu „błędu krytycznego: nie można utworzyć zakresu z końcem <start”, gdy zostanie przekazana pusta tablica.
Carl Smith
3
@ Jan: Tak, dodaj guard i != j else { continue }przed zamianą. Złożyłem radar, ale nowe zachowanie jest celowe.
Nate Cook,
3
W rzeczywistości shuffleInPlacemoże ulec awarii, jeśli indeksy kolekcji nie zaczynają się od zera, np. Dla wycinka tablicy. for i in 0..<count - 1 powinno być for i in startIndex ..< endIndex - 1(a wtedy konwersja do Swift 3 staje się prawie banalna).
Martin R
131

Edycja: Jak zauważono w innych odpowiedziach, Swift 4.2 w końcu dodaje generowanie liczb losowych do standardowej biblioteki wraz z tasowaniem tablicy.

Jednak GKRandom/ GKRandomDistributionSuite w GameplayKit może być nadal użyteczny z nowym RandomNumberGeneratorprotokołem - jeśli dodasz rozszerzenia do GameplayKit RNG w celu dostosowania do nowego standardowego protokołu biblioteki, możesz łatwo uzyskać:

  • wysyłane RNG (które mogą odtworzyć „losową” sekwencję, gdy jest to potrzebne do testowania)
  • RNG, które poświęcają solidność dla szybkości
  • RNG, które wytwarzają nierównomierne rozkłady

... i nadal korzystaj z nowych, „losowych” losowych interfejsów API w Swift.

Pozostała część odpowiedzi dotyczy takich RNG i / lub ich zastosowania w starszych kompilatorach Swift.


Jest już kilka dobrych odpowiedzi, a także kilka dobrych ilustracji, dlaczego pisanie własnego losowania może być podatne na błędy, jeśli nie jesteś ostrożny.

W iOS 9, macOS 10.11 i tvOS 9 (lub nowszy) nie musisz pisać własnego. W GameplayKit istnieje wydajna, poprawna implementacja Fisher-Yatesa (która, pomimo nazwy, nie jest przeznaczona tylko do gier).

Jeśli chcesz tylko wyjątkowego losowania:

let shuffled = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: array)

Jeśli chcesz być w stanie powielić losowanie lub serię losowania, wybierz i zapisz określone losowe źródło; na przykład

let lcg = GKLinearCongruentialRandomSource(seed: mySeedValue)
let shuffled = lcg.arrayByShufflingObjects(in: array)

W iOS 10 / macOS 10.12 / tvOS 10 istnieje również wygodna składnia do przetasowania za pomocą rozszerzenia na NSArray. Oczywiście jest to trochę kłopotliwe, gdy używasz Swift Array(i traci swój typ elementu po powrocie do Swift):

let shuffled1 = (array as NSArray).shuffled(using: random) // -> [Any]
let shuffled2 = (array as NSArray).shuffled() // use default random source

Ale dość łatwo jest stworzyć dla niego opakowanie Swift zachowujące typ:

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
źródło
6
Zastanawiam się, jakie inne przydatne narzędzia można znaleźć w GameplayKit, których nigdy nie odkryłem!
Richard Venable
6
Wyszukiwanie wykresów, wyszukiwanie drzew, systemy reguł ... wiele rzeczy, które są pomocne zarówno w projektowaniu gier, jak i poza nimi.
rickster,
5
W Swift 3 / iOS 10 zmieniono to na:let shuffled = lcg.arrayByShufflingObjects(in: array)
Evan Pon
30

W Swift 2.0 na pomoc może przyjść GameplayKit! (obsługiwany przez iOS9 lub nowszy)

import GameplayKit

func shuffle() {
    array = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(array)
}
bluenowhere
źródło
5
importowanie zestawu GameplayKit tylko po to, aby uzyskać losową tablicę, nie wydaje się świetnym pomysłem
Lope
3
Dlaczego? Jest to część systemu, nie dodaje się do pliku binarnego.
Abizern
3
Możesz również zawęzić zakres importu doimport GameplayKit.GKRandomSource
JRG-Developer
26

Oto coś, być może nieco krótszego:

sorted(a) {_, _ in arc4random() % 2 == 0}
Jean Le Moignan
źródło
1
@moby sortFunkcja wymaga zamknięcia, aby zamówić elementy. To zamknięcie przyjmuje dwa parametry (elem1, elem2) i musi zwrócić true, jeśli pierwsza wartość powinna pojawić się przed drugą wartością, a false w przeciwnym razie. Jeśli zamiast tego zwrócimy losową wartość logiczną ... to po prostu wszystko pomieszamy :)
Jean Le Moignan
2
Czy jest tu jakiś matematyk, który mógłby potwierdzić lub obalić?
Jean Le Moignan,
9
Jak zauważyli pjs w odpowiedzi na inną bardzo podobną odpowiedź, nie spowoduje to jednolitego rozkładu wyników. Użyj Shuffle Fisher-Yatesa, jak pokazano w odpowiedzi Nate'a Cooka.
Rob
1
To sprytna sztuczka, ale jest fatalna pod względem jakości przetasowania. Po pierwsze, to zamknięcie powinno się zastosować arc4random_uniform(), ponieważ obecnie podlega ono tendencyjności modulo. Po drugie, wynik zależy bardzo mocno od algorytmu sortowania (który nie jest nam znany bez spojrzenia na źródło).
Alexander - Przywróć Monikę
1
Kontynuując to prostsze podejście, wydaje się, że działa całkiem nieźle: collection.sorted { _,_ in arc4random_uniform(1) == 0 }
markiv
7

Biorąc algorytm Nate'a, chciałem zobaczyć, jak by to wyglądało z Swift 2 i rozszerzeniami protokołu.

Właśnie to wymyśliłem.

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

Teraz każdy MutableCollectionTypemoże korzystać z tych metod, ponieważ używa go IntjakoIndex

Chris Wagner
źródło
6

W moim przypadku miałem problemy z zamianą obiektów w szyku. Potem podrapałem się w głowę i postanowiłem odkryć koło.

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

}
Kaz Yoshikawa
źródło
5

To jest wersja implementacji losowania Fisher-Yates dla Swift 4 (Xcode 9) przez Nate'a .

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

Zmiany są następujące:

  • Ograniczenie Indices.Iterator.Element == Indexjest teraz częścią Collectionprotokołu i nie musi być już nakładane na rozszerzenie.
  • Wymianę elementów należy wykonać, wywołując swapAt()kolekcję, porównaj SE-0173 DodajMutableCollection.swapAt(_:_:) .
  • Elementjest pseudonimem dla Iterator.Element.
Martin R.
źródło
3

Oto, czego używam:

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
źródło
3

Swift 4 Przetasuj elementy tablicy w pętli for, w której i oznacza stosunek mieszania

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

Lub z rozszerzeniem 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
        }
    }
}
Siergiej
źródło
2

Rozwiązanie Swift 3, zgodne z odpowiedzią @Nate Cook: (działa, jeśli indeks zaczyna się od 0, patrz komentarze poniżej)

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])
        }
    }
}
Anson Yao
źródło
1
Może się to zawieszać, jeśli indeksy kolekcji zaczynają się od 0, np. Dla wycinka tablicy. Spróbuj uruchomić var a = [1, 2, 3, 4, 5, 6][3..<6]; a.shuffleInPlace()kilka razy. - Aby znaleźć prawidłowe rozwiązanie, zobacz stackoverflow.com/a/37843901/1187415
Martin R
2

Tak to się robi w najprostszy sposób. import Gamplaykitdo swojego VC i użyj poniższego kodu. Testowane w Xcode 8.

 import GameplayKit

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

 override func viewDidLoad() {
    super.viewDidLoad()

    print(array.shuffled())  
}

Jeśli chcesz uzyskać tasowany ciąg znaków z tablicy, możesz użyć poniższego kodu.

func suffleString() {

    let ShuffleArray = array.shuffled()

    suffleString.text = ShuffleArray.first as? String

    print(suffleString.text!)

}
Joe
źródło
2

W Swift 3, jeśli chcesz przetasować tablicę w miejscu lub uzyskać nową przetasowaną tablicę z tablicy, AnyIteratormoże ci pomóc. Chodzi o to, aby utworzyć tablicę indeksów z tablicy, przetasować te indeksy za pomocą AnyIteratorinstancji i swap(_:_:)funkcji oraz zmapować każdy element tego AnyIteratorwystąpienia za pomocą odpowiedniego elementu tablicy.


Poniższy kod placu zabaw pokazuje, jak to działa:

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

Możesz zmienić poprzedni kod i utworzyć shuffled()funkcję w Arrayrozszerzeniu, aby uzyskać nową tablicę losową z tablicy:

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

}

Stosowanie:

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: []

Alternatywnie do poprzedniego kodu możesz utworzyć shuffle()funkcję wewnątrz Arrayrozszerzenia, aby przetasować tablicę w miejscu:

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

}

Stosowanie:

var mutatingArray = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]
mutatingArray.shuffle()
print(mutatingArray) // may print ["Sue Ellen", "Pamela", "Jock", "Ellie", "Bobby", "JR"]
Imanou Petit
źródło
1

Możesz także użyć swapfunkcji ogólnej i zaimplementować wspomnianego Fisher-Yatesa:

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

lub mniej gadatliwie:

for idx in 0..<steps.count {
  swap(&steps[idx], &steps[Int(arc4random_uniform(UInt32(idx)))])
}
Daniel Bauke
źródło
2
Cierpi na tym przynajmniej poważny błąd opisany tutaj jednym błędem , w wyniku którego wartość jest zawsze zamieniana z pierwotnej pozycji. Można temu zaradzić let rnd = Int(arc4random_uniform(UInt32(idx + 1))). Ponadto w FY generalnie iterujesz od arr.count - 1dołu do 1(lub jeśli iterujesz od 0do arr.count - 1, wybierasz indeks jak Nate pokazuje w zaakceptowanej odpowiedzi). Zobacz sekcję Modern Algorytm dyskusji Fisher-Yatesa.
Rob
1

Pracuje!!. organizmy to tablica do przetasowania.

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
źródło
0

Oto jak przetasować jedną tablicę z ziarnem w 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
źródło
0
let shuffl = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: arrayObject)
Rohit Sisodia
źródło
0

Oto, czego używam:

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]
Daniel Illescas
źródło
0

Prosty przykład:

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]
Konstabl
źródło
0

Rozszerzenie macierzy roboczej (mutowanie i niemutowanie)

Swift 4.1 / Xcode 9

Najlepsza odpowiedź jest przestarzała, więc postanowiłem stworzyć własne rozszerzenie, aby przetasować tablicę w najnowszej wersji 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
    }
}

Zadzwoń bez mutacji [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)

To drukuje arrayw losowej kolejności.


Wywołanie losowego mutowania [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) 

Drukuje arrayw bieżącej kolejności, która została już losowo przetasowana.


Mam nadzieję, że to zadziała dla wszystkich, jeśli masz jakieś pytania, sugestie lub komentarze, możesz je zadać!

Noah Wilder
źródło
0

W 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
}
Ali Chezri
źródło
0

Jeśli chcesz użyć prostej funkcji pętli Swift For, użyj tego ->

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 suffle przy użyciu rozszerzenia ->

extension Array {
    // Order Randomize
    mutating func shuffle() {
        for _ in 0..<count {
            sort { (_,_) in arc4random() < arc4random() }
        }
    }
}
Rahul Singha Roy
źródło
0

Począwszy od wersji 4.2, są dwie przydatne funkcje:

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

i

// generates a new array with shuffled elements of the old array
let newArray = myArray.shuffled()
szybki ryś
źródło
-2

Oto kod działający na placu zabaw. Nie musisz importować Darwina do rzeczywistego projektu 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)
Dan Hixon
źródło
7
Daje to nierównomierny rozkład wyników. Będzie to również O (n log n), gdzie losowanie Fisher-Yates dałoby równomiernie rozłożone wyniki w czasie O (n).
pjs
drand48()Daje również te same pseudolosowe liczby za każdym razem, chyba że ustawisz seed za pomocą likesrand48(Int(arc4random()))
Kametrixom
-3

Zatrzymuje się na „swap (i self [i], i self [j])”, kiedy aktualizuję wersję xCode do wersji 7.4 beta.
błąd krytyczny: zamiana lokalizacji z samym sobą nie jest obsługiwana

Znalazłem przyczynę, że i = j (funkcja zamiany wybuchnie)

Więc dodaję warunek jak poniżej

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

YA! Dla mnie ok.

米米米
źródło
To wydaje się być komentarzem do odpowiedzi Chrisa , a nie odpowiedzią na pierwotne pytanie.
Mogsdad