我如何在Swift洗牌数组?

.shuffle()和.shuffled()是Swift的一部分


原历史问题:

我如何随机或洗牌在Swift数组中的元素?例如,如果我的数组包含52张扑克牌,我想洗牌数组以便洗牌。

98511 次浏览

这是一些在playground上运行的代码。你不需要在实际的Xcode项目中导入Darwin。

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)

这个答案详细介绍了如何在Swift 4.2+中使用快速和统一的算法(Fisher-Yates)进行洗牌,以及如何在之前的各种Swift版本中添加相同的功能。每个Swift版本的命名和行为都与该版本的突变和非突变排序方法相匹配。

斯威夫特4.2 +

shuffleshuffled在Swift 4.2开始时是原生的。使用示例:

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

这些扩展将shuffle()方法添加到任何可变集合(数组和不安全的可变缓冲区),并将shuffled()方法添加到任何序列:

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

用法与上述Swift 4.2示例相同。


斯威夫特3

这些扩展将shuffle()方法添加到任何可变集合,并将shuffled()方法添加到任何序列:

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

用法与上述Swift 4.2示例相同。


斯威夫特2

(过时的语言:你不能使用Swift 2。x将于2018年7月在iTunes Connect上发布)

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

用法:

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

斯威夫特1.2

(过时的语言:你不能使用Swift 1。x将于2018年7月在iTunes Connect上发布)

shuffle作为一个突变数组方法

这个扩展将让你洗牌一个可变的Array实例:

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作为一个非突变数组方法

这个扩展将让你检索Array实例的打乱副本:

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]

这里有一些可能更短的内容:

sorted(a) {_, _ in arc4random() % 2 == 0}

你也可以使用泛型swap函数并实现前面提到的Fisher-Yates:

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

或者更简洁:

for idx in 0..<steps.count {
swap(&steps[idx], &steps[Int(arc4random_uniform(UInt32(idx)))])
}

这是我所使用的:

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
}

内特的算法为例,我想看看这在Swift 2和协议扩展中会是什么样子。

这是我想到的。

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

现在,任何MutableCollectionType都可以使用这些方法,前提是它使用Int作为Index

正如在其他答案中提到的,Swift 4.2 最后在标准库中添加了随机数生成,并完成了数组洗牌。

然而,GameplayKit中的GKRandom / GKRandomDistribution套件仍然可以用于新的RandomNumberGenerator协议-如果你添加扩展到GameplayKit rng以符合新的标准库协议,你可以很容易地获得:

  • 可发送的rng(在测试需要时可以复制“随机”序列)
  • 为了速度而牺牲健壮性的rng
  • 产生非均匀分布的rng

...并且仍然可以使用Swift中漂亮的新“本地”随机api。

这个答案的其余部分涉及到这些rng和/或它们在旧的Swift编译器中的使用。


这里已经有一些很好的答案,以及一些很好的说明为什么如果不小心,编写自己的shuffle可能容易出错。

在iOS 9、macOS 10.11和tvOS 9(或更高版本)中,你不必自己编写。GameplayKit中有一个有效的,正确的执行费雪-耶茨(尽管名字如此,但并不只是用于游戏)。

如果你只是想要一个独特的洗牌:

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

如果您希望能够复制洗牌或一系列洗牌,请选择并播种一个特定的随机源;如。

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

在iOS 10 / macOS 10.12 / tvOS 10中,也有一个方便的语法,可以通过NSArray上的扩展进行变换。当然,当你使用Swift Array时,这有点麻烦(并且它在返回Swift时失去其元素类型):

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

但是为它创建一个保持类型的Swift包装器非常简单:

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

它停止在“swap(&self[i], &self[j])”当我升级xCode版本到7.4 beta 致命错误:不支持与自身交换位置

我找到了I = j的原因(交换函数将爆炸)

所以我添加了一个条件,如下所示

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

丫!对我来说没问题。

在Swift 3中,如果你想洗牌一个数组,或者从一个数组中获得一个新的洗牌数组,AnyIterator可以帮助你。其思想是从数组中创建一个索引数组,用AnyIterator实例和swap(_:_:)函数洗牌这些索引,并将这个AnyIterator实例的每个元素映射到数组的相应元素。


下面的Playground代码展示了它是如何工作的:

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

你可以重构前面的代码,并在Array扩展中创建一个shuffled()函数,以便从数组中获得一个新的打乱数组:

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


}

用法:

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

作为前面代码的替代方法,你可以在Array扩展中创建一个shuffle()函数,以便在适当的位置洗牌数组:

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


}

用法:

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

斯威夫特2.0中,GameplayKit可能会来拯救!(由iOS9或更高版本支持)

import GameplayKit


func shuffle() {
array = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(array)
}

Swift 3解决方案,遵循@Nate Cook的回答:(如果索引从0开始,请参阅下面的评论)

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

这是最简单的方法。import Gamplaykit到你的VC并使用下面的代码。在Xcode 8中测试。

 import GameplayKit


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


override func viewDidLoad() {
super.viewDidLoad()


print(array.shuffled())
}

如果你想从数组中获得一个打乱的字符串,你可以使用下面的代码..

func suffleString() {


let ShuffleArray = array.shuffled()


suffleString.text = ShuffleArray.first as? String


print(suffleString.text!)


}

在我的例子中,我在数组中交换对象时遇到了一些问题。然后我挠头,开始重新发明轮子。

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


}

这是如何在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])
}
}
}
let shuffl = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: arrayObject)

这是Swift 4的内特实施的费雪-耶茨洗牌的一个版本 (Xcode 9)。< / p >

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

这些变化是:

    约束Indices.Iterator.Element == Index现在是part Collection协议的 李扩展了。< / > 交换元素必须通过调用集合上的swapAt()来完成, 李比较SE-0173添加MutableCollection.swapAt(_:_:)。< / >
  • ElementIterator.Element的别名。

这是我所使用的:

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]

简单的例子:

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]

工作! !生物体是要洗牌的数组。

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

< >强劲迅速4 在for循环中洗牌数组的元素,其中i是混合比

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

或者扩展为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
}
}
}

工作阵列扩展(突变&non-mutating)

Swift 4.1 / Xcode 9

上面的答案已弃用,所以我自己创建了自己的扩展,在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
}
}

调用非突变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)

这将以随机顺序打印array


调用可变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)

这将以当前顺序打印array,该顺序已经被随机打乱。


希望这对每个人都有用,如果你有任何问题,建议或评论,请随时提问!

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
}

斯威夫特4.2中,现在有一个可变shuffle不可变的shuffled的方法。你可以阅读更多关于随机生成和数组的东西在这里

如果你想使用简单的Swift For循环函数,使用这个->

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使用扩展名->

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

swift 4.2有两个方便的功能:

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

而且

// generates a new array with shuffled elements of the old array
let newArray = myArray.shuffled()