Como embaralhar uma matriz no Swift?

305

Como faço para aleatorizar ou embaralhar os elementos em uma matriz no Swift? Por exemplo, se minha matriz consiste em 52 cartas, quero embaralhar a matriz para embaralhar o baralho.

mpatzer
fonte
2
isso não é específico para nenhum idioma. Basta aplicar qualquer algoritmo de embaralhamento ...
Gabriele Petronella
8
@Mithrandir Isso não é verdade. Em Ruby, alguém procuraria array.shuffle. Não há necessidade de implementar sua própria versão. Eu acho que o OP estava procurando por algo semelhante.
Linus Oleander
1
tenha cuidado, no entanto, não use qualquer algoritmo de reprodução aleatória para embaralhar um baralho de cartas.
Njzk2 17/10/19

Respostas:

627

Esta resposta detalha como embaralhar com um algoritmo rápido e uniforme (Fisher-Yates) no Swift 4.2+ e como adicionar o mesmo recurso nas várias versões anteriores do Swift. A nomeação e o comportamento de cada versão do Swift correspondem aos métodos de classificação com e sem mutação para essa versão.

Swift 4.2+

shufflee shuffledsão nativos a partir do Swift 4.2. Exemplo de uso:

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

Essas extensões adicionam um shuffle()método a qualquer coleção mutável (matrizes e buffers mutáveis ​​inseguros) e um shuffled()método a qualquer sequência:

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

O mesmo uso dos exemplos do Swift 4.2 acima.


Swift 3

Essas extensões adicionam um shuffle()método a qualquer coleção mutável e um shuffled()método a qualquer sequência:

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

O mesmo uso dos exemplos do Swift 4.2 acima.


Swift 2

(idioma obsoleto: você não pode usar o Swift 2.x para publicar no iTunes Connect a partir de julho de 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
    }
}

Uso:

[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

(idioma obsoleto: você não pode usar o Swift 1.x para publicar no iTunes Connect a partir de julho de 2018)

shuffle como um método de matriz mutante

Esta extensão permitirá embaralhar uma Arrayinstância mutável no lugar:

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 como um método de matriz não mutante

Esta extensão permitirá recuperar uma cópia aleatória de uma Arrayinstância:

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
fonte
1
Caso você deseje a versão da função no Swift 1.2, ela precisa de um pouco de atualização à medida que countElementsdesaparece, e sua substituição countagora retorna um T.Index.Distancepara que a restrição precise estar ativa C.Index.Distance == Int. Esta versão deve funcionar: gist.github.com/airspeedswift/03d07a9dc86fabdc370f
Airspeed Velocity
2
Essa é a saída real - Fisher-Yates deve retornar uma permutação aleatória imparcial da fonte, portanto não há exigência de que um elemento específico se mova. Não é uma garantia de que nenhum elemento se move mais de uma vez, mas às vezes o "movimento" é o mesmo índice. O caso mais simples é pensar [1, 2].shuffled()- isso deve retornar [2, 1]sempre?
Nate Cook
1
Eu adicionei if count > 0na parte superior da função de matriz mutante, para evitar o recebimento de um "erro fatal: não é possível formar Range com end <start" quando é passada uma matriz vazia.
Carl Smith
3
@ Jan: Sim, adicione guard i != j else { continue }antes da troca. Arquivei um radar, mas o novo comportamento é intencional.
Nate Cook
3
Na verdade, shuffleInPlacepode travar se os índices de coleta não começarem em zero, por exemplo, para uma fatia da matriz. for i in 0..<count - 1 deve ser for i in startIndex ..< endIndex - 1(e a conversão para o Swift 3 se torna quase trivial).
Martin R
131

Edit: Como observado em outras respostas, o Swift 4.2 finalmente adiciona geração aleatória de números à biblioteca padrão, completa com a reprodução aleatória da matriz.

No entanto, o GKRandom/ GKRandomDistributionsuite no GameplayKit ainda pode ser útil com o novo RandomNumberGeneratorprotocolo - se você adicionar extensões aos RNGs do GameplayKit para estar em conformidade com o novo protocolo de biblioteca padrão, poderá obter facilmente:

  • RNGs enviados (que podem reproduzir uma sequência "aleatória" quando necessário para teste)
  • RNGs que sacrificam robustez por velocidade
  • RNGs que produzem distribuições não uniformes

... e ainda faça uso das novas e agradáveis ​​APIs aleatórias "nativas" no Swift.

O restante desta resposta diz respeito a esses RNGs e / ou seu uso em compiladores Swift mais antigos.


Já existem boas respostas aqui, bem como algumas boas ilustrações de por que escrever seu próprio shuffle pode ser propenso a erros se você não tomar cuidado.

No iOS 9, macOS 10.11 e tvOS 9 (ou posterior), você não precisa escrever por conta própria. Há uma implementação eficiente e correta de Fisher-Yates no GameplayKit (que, apesar do nome, não é apenas para jogos).

Se você quer apenas um shuffle exclusivo:

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

Se você deseja replicar um shuffle ou uma série de shuffles, escolha e propague uma fonte aleatória específica; por exemplo

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

No iOS 10 / macOS 10.12 / tvOS 10, também há uma sintaxe de conveniência para embaralhar por meio de uma extensão NSArray. Claro, isso é um pouco complicado quando você usa um Swift Array(e perde o tipo de elemento ao voltar ao Swift):

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

Mas é muito fácil criar um wrapper Swift que preserva o tipo:

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
fonte
6
Me faz pensar que outros utilitários úteis podem ser encontrados no GameplayKit que eu nunca explorei!
Richard Venable
6
Pesquisa de gráficos, pesquisa em árvore, sistemas de regras ... muitas coisas úteis tanto no design de jogos quanto em outros aspectos.
Rickster 26/10/2015
5
No Swift 3 / iOS 10, isso foi alterado para:let shuffled = lcg.arrayByShufflingObjects(in: array)
Evan Pon
30

No Swift 2.0 , o GameplayKit pode ajudar! (suportado pelo iOS9 ou posterior)

import GameplayKit

func shuffle() {
    array = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(array)
}
bluenowhere
fonte
5
importação GameplayKit apenas para obter variedade baralhados não soa como uma grande idéia
Lope
3
Por quê? Faz parte do sistema, não adiciona ao binário.
Abizern
3
Você também pode definir a importação de forma simplesimport GameplayKit.GKRandomSource
JRG-Developer
26

Aqui está algo possivelmente um pouco mais curto:

sorted(a) {_, _ in arc4random() % 2 == 0}
Jean Le Moignan
fonte
1
@moby A sortfunção precisa de um fechamento para solicitar elementos. Esse fechamento usa dois parâmetros (elem1, elem2) e deve retornar true se o primeiro valor aparecer antes do segundo valor e false caso contrário. Se voltarmos um booleano aleatória vez ... então nós apenas misturar a coisa toda :)
Jean Le Moignan
2
Algum matemático aqui para confirmar ou refutar?
Jean Le Moignan
9
Como pjs apontou em resposta a outra resposta muito semelhante, isso não gerará distribuição uniforme de resultados. Use Fisher-Yates Shuffle, como mostrado na resposta de Nate Cook.
Rob
1
Este é um truque inteligente, mas é péssimo em termos de qualidade do shuffle. Por um lado, esse fechamento deve ser utilizado arc4random_uniform(), pois atualmente está sujeito ao viés do módulo. Em segundo lugar, a saída depende muito do algoritmo de classificação (que não é conhecido por nós sem observar a fonte).
Alexander - Restabelece Monica
1
Continuando com esta abordagem mais simples, isso parece funcionar muito bem: collection.sorted { _,_ in arc4random_uniform(1) == 0 }
markiv
7

Tomando o algoritmo de Nate, eu queria ver como isso ficaria com o Swift 2 e as extensões de protocolo.

Isto é o que eu vim com.

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

Agora, qualquer um MutableCollectionTypepode usar esses métodos, pois usa Intcomo umIndex

Chris Wagner
fonte
6

No meu caso, tive alguns problemas ao trocar objetos no Array. Então coçei minha cabeça e fui reinventar a roda.

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

Esta é uma versão da implementação de Nate do shuffle Fisher-Yates para 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
    }
}

As mudanças são:

  • A restrição Indices.Iterator.Element == Indexagora faz parte do Collectionprotocolo e não precisa mais ser imposta à extensão.
  • A troca de elementos deve ser feita chamando swapAt()a coleção, compare SE-0173 AddMutableCollection.swapAt(_:_:) .
  • Elementé um alias para Iterator.Element.
Martin R
fonte
3

Isto é o que eu uso:

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
fonte
3

Swift 4 Embaralhe os elementos de uma matriz em um loop for, onde i é a taxa de mixagem

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

Ou com extensão 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
        }
    }
}
Sergei
fonte
2

Solução Swift 3, seguindo a resposta @Nate Cook: (funcione se o índice começar com 0, veja os comentários abaixo)

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
fonte
1
Isso pode falhar se os índices de coleção começarem em 0, por exemplo, para uma fatia da matriz. Tente executar var a = [1, 2, 3, 4, 5, 6][3..<6]; a.shuffleInPlace()várias vezes. - Consulte stackoverflow.com/a/37843901/1187415 para obter uma solução correta.
Martin R
2

É assim que é feito da maneira mais simples. import Gamplaykitao seu VC e use o código abaixo. Testado no Xcode 8.

 import GameplayKit

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

 override func viewDidLoad() {
    super.viewDidLoad()

    print(array.shuffled())  
}

Se você deseja obter uma String embaralhada de uma Matriz, pode usar o código abaixo.

func suffleString() {

    let ShuffleArray = array.shuffled()

    suffleString.text = ShuffleArray.first as? String

    print(suffleString.text!)

}
Joe
fonte
2

Com o Swift 3, se você deseja embaralhar uma matriz no local ou obter uma nova matriz embaralhada de uma matriz, AnyIteratorpode ajudá-lo. A idéia é criar uma matriz de índices a partir da matriz, embaralhá-los com uma AnyIteratorinstância e swap(_:_:)função e mapear cada elemento dessa AnyIteratorinstância com o elemento correspondente da matriz.


O seguinte código do Playground mostra como ele funciona:

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

Você pode refatorar o código anterior e criar uma shuffled()função dentro de uma Arrayextensão para obter uma nova matriz embaralhada de uma matriz:

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

}

Uso:

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

Como alternativa ao código anterior, você pode criar uma shuffle()função dentro de uma Arrayextensão para embaralhar uma matriz no lugar:

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

}

Uso:

var mutatingArray = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]
mutatingArray.shuffle()
print(mutatingArray) // may print ["Sue Ellen", "Pamela", "Jock", "Ellie", "Bobby", "JR"]
Imanou Petit
fonte
1

Você também pode usar a swapfunção genérica e implementar Fisher-Yates mencionados:

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

ou menos detalhado:

for idx in 0..<steps.count {
  swap(&steps[idx], &steps[Int(arc4random_uniform(UInt32(idx)))])
}
Daniel Bauke
fonte
2
Isso sofre, no mínimo, com um erro grave descrito aqui, no qual um valor é sempre trocado de sua posição original. Isso é remediado com let rnd = Int(arc4random_uniform(UInt32(idx + 1))). Além disso, no EF, você geralmente itera de arr.count - 1baixo para 1(ou, se você iterar de 0para arr.count - 1, escolhe um índice como o Nate mostra na resposta aceita). Veja a seção Algoritmo Moderno da discussão de Fisher-Yates.
22414 Rob às
1

trabalho!!. organismos é a matriz para embaralhar.

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
fonte
0

É assim que você embaralha uma matriz com uma semente no 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
fonte
0
let shuffl = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: arrayObject)
Rohit Sisodia
fonte
0

Isto é o que eu uso:

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
fonte
0

Exemplo simples:

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]
Bobby
fonte
0

Extensão de matriz de trabalho (com e sem mutação)

Swift 4.1 / Xcode 9

A resposta principal foi descontinuada, então decidi criar minha própria extensão para embaralhar uma matriz na versão mais recente do 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
    }
}

Chamada aleatória sem mutação de chamada [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)

Isso é impresso arrayem uma ordem aleatória.


Aleatório de mutação de chamada [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) 

Isso é impresso arrayem sua ordem atual, que já foi aleatoriamente embaralhada.


Espero que isso funcione para todos, se você tiver quaisquer perguntas, sugestões ou comentários, não hesite em perguntar!

Noah Wilder
fonte
0

No 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 khezri
fonte
0

Se você deseja usar a função de loop Swift For simples, use isto ->

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 é suficiente usando a extensão ->

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

No Swift 4.2, existem duas funções úteis:

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

e

// generates a new array with shuffled elements of the old array
let newArray = myArray.shuffled()
lince rápido
fonte
-2

Aqui está um código que é executado no playground. Você não precisará importar o Darwin em um projeto real do 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
fonte
7
Isso fornece uma distribuição não uniforme dos resultados. Também será O (n log n), onde um embaralhamento de Fisher-Yates daria resultados uniformemente distribuídos em O (n) tempo.
pjs
Também drand48()dá o mesmo pseudo números aleatórios toda vez que, a menos que você definir uma semente com gostosrand48(Int(arc4random()))
Kametrixom
-3

Ele pára em "swap (& self [i], & self [j])" quando atualizo a versão xCode para 7.4 beta.
erro fatal: a troca de um local consigo mesma não é suportada

Encontrei o motivo pelo qual i = j (a função de troca explodiu)

Então eu adiciono uma condição como abaixo

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

YA! Tudo bem para mim.

米米米
fonte
Parece ser um comentário à resposta de Chris , não uma resposta à pergunta original.
Mogsdad