随机排列数组 swift 3

Shuffle array swift 3

如何将下面的函数转换为 swift 3?当前出现 Binary operator '..<' cannot be applied to operands of type 'Int' and 'Self.IndexDistance' 错误。

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 }

    for i in 0..<count - 1 { //error takes place here
      let j = Int(arc4random_uniform(UInt32(count - i))) + i
      guard i != j else { continue }
      swap(&self[i], &self[j])
    }
  }
}

参考:

我建议简单地改组数组而不是尝试将其扩展到一般集合:

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

count returns 一个 IndexDistance 这是描述的类型 两个集合索引之间的距离。 IndexDistance 是 必须是 SignedInteger,但不必是 Int,并且可以 不同于 Index。因此无法创建 范围 0..<count - 1.

一个解决方案是使用 startIndexendIndex 而不是 0count:

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

另一个优点是这也适用于数组 slices (第一个元素的索引不一定为零)。

请注意,根据新的 "Swift API Design Guidelines"shuffle() 是变异洗牌方法的 "proper" 名称, 和 shuffled() 对于 returns 一个数组的非变异对应物:

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

更新:A(更通用)Swift3版本已添加 How do I shuffle an array in 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)
        }
    }
}

有关 swapAt 的更多信息,请参阅 SE-0173 Add MutableCollection.swapAt(_:_:)


Swift 4.2(Xcode 10,目前处于测试阶段),随着 SE-0202 Random Unification, shuffle()shuffled() 是 Swift 标准库的一部分。

Gamekit 中有一个 fisher-yates shuffle:

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)

为此,您可以使用 GameplayKit 框架中的 NSArray 扩展:

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]