【问题标题】:Shuffle array swift 3随机播放数组 swift 3
【发布时间】:2016-06-15 18:56:13
【问题描述】:

如何将下面的函数转换为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])
    }
  }
}

参考:https://stackoverflow.com/a/24029847/5222077

【问题讨论】:

标签: swift swift3


【解决方案1】:

count 返回一个IndexDistance,它是描述的类型 两个集合索引之间的距离。 IndexDistance 是 必须是SignedInteger,但不必是Int,并且可以 不同于Index。因此无法创建 范围0..&lt;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() 是 mutating shuffle 方法的“正确”名称, 和shuffled() 用于返回数组的非变异对应物:

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

更新: 一个(更通用的)Swift 3 版本已添加到 同时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 标准库的一部分。

【讨论】:

  • 只是觉得我应该指出let j 中的+ i 可能应该是+ startIndex。否则这很可能导致索引超出范围。
  • @BjarkeH.Søndergaard:我相当确定上面的代码是正确的,并且我已经用数组和数组切片对其进行了测试。 istartIndex..&lt;endIndex-1范围内,0..&lt;endIndex-i范围内的一个随机数被添加到i,这样j &gt;= i &gt;= startIndexj &lt; i + (endIndex-i) = endIndex
  • 啊,我现在知道我在哪里读错了您的代码。抱歉,现在还是凌晨。我把你的 j 读成endIndex-1 而不是endIndex-i。 :)
  • 在我看来 startIndex..
  • @MarkDail: 如果i == endIndex-1 然后j == i 将是唯一可能的交换索引,因此如果i 运行到endIndex-2 就足够了
【解决方案2】:

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)

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      您可以为此使用 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]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-07
        • 1970-01-01
        • 2011-01-27
        • 2023-03-10
        相关资源
        最近更新 更多