How do I shuffle an array in Swift?

2018-12-31 00:43发布

How do I randomize or shuffle the elements within an array in Swift? For example, if my array consists of 52 playing cards, I want to shuffle the array in order to shuffle the deck.

25条回答
唯独是你
2楼-- · 2018-12-31 01:27

This is how to shuffle one array with a seed in 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])
        }
    }
}
查看更多
不再属于我。
3楼-- · 2018-12-31 01:30

This is what I use:

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
}
查看更多
呛了眼睛熬了心
4楼-- · 2018-12-31 01:32

Edit: As noted in other answers, Swift 4.2 finally adds random number generation to the standard library, complete with array shuffling.

However, the GKRandom / GKRandomDistribution suite in GameplayKit can still be useful with the new RandomNumberGenerator protocol — if you add extensions to the GameplayKit RNGs to conform to the new standard library protocol, you can easily get:

  • sendable RNGs (that can reproduce a "random" sequence when needed for testing)
  • RNGs that sacrifice robustness for speed
  • RNGs that produce non-uniform distributions

...and still make use of the nice new "native" random APIs in Swift.

The rest of this answer concerns such RNGs and/or their use in older Swift compilers.


There are some good answers here already, as well as some good illustrations of why writing your own shuffle can be error-prone if you're not careful.

In iOS 9, macOS 10.11, and tvOS 9 (or later), you don't have to write your own. There's an efficient, correct implementation of Fisher-Yates in GameplayKit (which, despite the name, is not just for games).

If you just want a unique shuffle:

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

If you want to be able to replicate a shuffle or series of shuffles, choose and seed a specific random source; e.g.

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

In iOS 10 / macOS 10.12 / tvOS 10, there's also a convenience syntax for shuffling via an extension on NSArray. Of course, that's a little cumbersome when you're using a Swift Array (and it loses its element type on coming back to Swift):

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

But it's pretty easy to make a type-preserving Swift wrapper for it:

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()
查看更多
像晚风撩人
5楼-- · 2018-12-31 01:32

Working Array Extension (mutating & non-mutating)

Swift 4.1 / Xcode 9

The top answer is deprecated, so I took it upon myself to create my own extension to shuffle an array in the newest version of 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
    }
}

Call Non-Mutating Shuffle [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)

This prints array in a random order.


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) 

This prints array in its current order, which has already been randomly shuffled.


Hopes this works for everybody, if you have any questions, suggestions, or comments, feel free to ask!

查看更多
无色无味的生活
6楼-- · 2018-12-31 01:32

In 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
}
查看更多
不流泪的眼
7楼-- · 2018-12-31 01:32

It stop at "swap(&self[i], &self[j])" when I upgrade the xCode version to 7.4 beta.
fatal error: swapping a location with itself is not supported

I found the reason that i = j (function of swap will exploded)

So I add a condition as below

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

YA! It's OK for me.

查看更多
登录 后发表回答