提问人:M-P 提问时间:6/4/2014 最后编辑:FattieM-P 更新时间:11/17/2022 访问量:101411
如何在 Swift 中随机排列数组?
How do I shuffle an array in Swift?
问:
.shuffle() 和 .shuffled() 是 Swift 的一部分
原始历史问题:
如何在 Swift 中随机化或随机排列数组中的元素?例如,如果我的阵列由 52 张扑克牌组成,我想洗牌阵列以洗牌。
答:
下面是在 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)
评论
drand48()
srand48(Int(arc4random()))
这个答案详细介绍了如何在 Swift 4.2+ 中使用快速统一的算法 (Fisher-Yates) 进行洗牌,以及如何在之前的各种 Swift 版本中添加相同的功能。每个 Swift 版本的命名和行为都与该版本的突变和非突变排序方法相匹配。
斯威夫特 4.2+
shuffle
和 shuffled
是 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
(过时的语言:从 2018 年 7 月开始,您无法使用 Swift 2.x 在 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
(过时的语言:从 2018 年 7 月开始,您无法使用 Swift 1.x 在 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]
评论
countElements
count
T.Index.Distance
C.Index.Distance == Int
[1, 2].shuffled()
[2, 1]
if count > 0
guard i != j else { continue }
shuffleInPlace
for i in 0..<count - 1
for i in startIndex ..< endIndex - 1
这里有一些可能更短的东西:
sorted(a) {_, _ in arc4random() % 2 == 0}
评论
sort
arc4random_uniform()
collection.sorted { _,_ in arc4random_uniform(1) == 0 }
您也可以使用泛型函数并实现上述 Fisher-Yates:swap
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
}
以 Nate 的算法为例,我想看看 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 中的 / suite 对于新协议仍然有用——如果您向 GameplayKit RNG 添加扩展以符合新的标准库协议,则可以轻松获得:GKRandom
GKRandomDistribution
RandomNumberGenerator
- 可发送的 RNG(在需要测试时可以重现“随机”序列)
- 为了速度而牺牲鲁棒性的 RNG
- 产生不均匀分布的 RNG
...并且仍然使用 Swift 中新的“原生”随机 API。
这个答案的其余部分涉及此类 RNG 和/或它们在旧版 Swift 编译器中的使用。
这里已经有一些很好的答案,也有一些很好的说明,说明为什么如果你不小心,编写自己的随机播放很容易出错。
在 iOS 9、macOS 10.11 和 Apple tvOS 9(或更高版本)中,您无需编写自己的内容。GameplayKit 中有一个高效、正确的 Fisher-Yates 实现(尽管它的名字不仅适用于游戏)。
如果你只是想要一个独特的洗牌:
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 中,还有一个方便的语法,可以通过扩展程序进行随机播放。当然,当你使用 Swift 时,这有点麻烦(它会在返回 Swift 时失去它的元素类型):NSArray
Array
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()
评论
let shuffled = lcg.arrayByShufflingObjects(in: array)
当我将 xCode 版本升级到 7.4 beta 时,它停在“swap(&self[i], &self[j])”。
致命错误:不支持将位置与自身交换
我找到了 i = j 的原因(swap 的函数会爆炸)
所以我添加一个条件,如下所示
if (i != j){
swap(&list[i], &list[j])
}
你!这对我来说没关系。
评论
在 Swift 3 中,如果你想对数组进行随机排序,或者从数组中获取一个新的随机数组,AnyIterator
可以帮到你。这个想法是从你的数组创建一个索引数组,用一个实例和 swap(_:_:)
函数洗牌这些索引,并将这个实例的每个元素与数组的相应元素映射。AnyIterator
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"]
您可以重构前面的代码并在扩展中创建一个函数,以便从数组中获取新的无序数组:shuffled()
Array
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: []
作为前面代码的替代方法,您可以在扩展中创建一个函数,以便就地随机排列数组:shuffle()
Array
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"]
在 Swift 2.0 中,GameplayKit 可能会派上用场!(iOS9 或更高版本支持)
import GameplayKit
func shuffle() {
array = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(array)
}
评论
import GameplayKit.GKRandomSource
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])
}
}
}
评论
var a = [1, 2, 3, 4, 5, 6][3..<6]; a.shuffleInPlace()
这就是它以最简单的方式完成的方式。添加到您的 VC 并使用以下代码。在 Xcode 8 中测试。import Gamplaykit
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)
这是 Nate 为 Swift 4 (Xcode 9) 实现的 Fisher-Yates shuffle 的一个版本。
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
Collection
- 交换元素必须通过调用集合来完成,
比较 SE-0173 添加
MutableCollection.swapAt(_:_:)
。swapAt()
Element
是 的别名。Iterator.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
}
}
}
工作阵列扩展(突变和非突变)
斯威夫特 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
}
}
调用非突变洗牌:[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
呼叫变异洗牌:[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
}
在 Swift 4.2 中,现在有一种既可以进行可变洗牌
又可以进行不可变洗牌
的方法。你可以在这里阅读更多关于随机生成和数组的信息。
如果你想使用简单的 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 suffle
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()
上一个:如何初始化相互依赖的属性
下一个:Swift - 编码 URL
评论
array.shuffle