从swift中获取数组中的随机元素
我有一个数组像:
var names: String = [ "Peter", "Steve", "Max", "Sandra", "Roman", "Julia" ]
我想从该数组中获得3个随机元素。 我来自C#,但迅速,我不确定从哪里开始。 我想我应该洗牌arrays,然后从中select前3个项目,例如?
我试图用下面的扩展名来洗牌:
extension Array { mutating func shuffle() { for _ in 0..<10 { sort { (_,_) in arc4random() < arc4random() } } } }
但是它在“shuffle()”的位置上表示“'()”不能转换为“[Int]”。
为了挑选我使用的一些元素:
var randomPicks = names[0..<4];
目前看起来不错。
如何洗牌? 还是有没有人有一个更好/更优雅的解决scheme呢?
Xcode 9•Swift 4
extension Array { /// Returns an array containing this sequence shuffled var shuffled: Array { var elements = self return elements.shuffle() } /// Shuffles this sequence in place @discardableResult mutating func shuffle() -> Array { let count = self.count indices.lazy.dropLast().forEach { swapAt($0, Int(arc4random_uniform(UInt32(count - $0))) + $0) } return self } var chooseOne: Element { return self[Int(arc4random_uniform(UInt32(count)))] } func choose(_ n: Int) -> Array { return Array(shuffled.prefix(n)) } }
游乐场testing
var alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"] let shuffledAlphabet = alphabet.shuffled let letter = alphabet.chooseOne var numbers = Array(0...9) let shuffledNumbers = numbers.shuffled shuffledNumbers // [8, 9, 3, 6, 0, 1, 4, 2, 5, 7] numbers // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] numbers.shuffle() // mutate it [6, 0, 2, 3, 9, 1, 5, 7, 4, 8] numbers // [6, 0, 2, 3, 9, 1, 5, 7, 4, 8] let pick3numbers = numbers.choose(3) // [8, 9, 2]
Xcode 8.3.1•Swift 3.1
import UIKit extension Array { /// Returns an array containing this sequence shuffled var shuffled: Array { var elements = self return elements.shuffle() } /// Shuffles this sequence in place @discardableResult mutating func shuffle() -> Array { let count = self.count indices.lazy.dropLast().forEach { guard case let index = Int(arc4random_uniform(UInt32(count - $0))) + $0, index != $0 else { return } swap(&self[$0], &self[index]) } return self } var chooseOne: Element { return self[Int(arc4random_uniform(UInt32(count)))] } func choose(_ n: Int) -> Array { return Array(shuffled.prefix(n)) } }
还是有没有人有一个更好/更优雅的解决scheme呢?
我做。 algorithm上比接受的答案更好,它对整个随机进行count-1 arc4random_uniform
操作,我们可以在n个 arc4random_uniform
操作中简单地选取n个值。
实际上,我有两种比接受的答案更好的方法:
解决scheme更好
extension Array { /// Picks `n` random elements (straightforward approach) subscript (randomPick n: Int) -> [Element] { var indices = [Int](0..<count) var randoms = [Int]() for _ in 0..<n { randoms.append(indices.remove(at: Int(arc4random_uniform(UInt32(indices.count))))) } return randoms.map { self[$0] } } }
最佳解决scheme
以下解决scheme比以前的解决scheme快两倍。
对于Swift 3.0和3.1
extension Array { /// Picks `n` random elements (partial Fisher-Yates shuffle approach) subscript (randomPick n: Int) -> [Element] { var copy = self for i in stride(from: count - 1, to: count - n - 1, by: -1) { let j = Int(arc4random_uniform(UInt32(i + 1))) if j != i { swap(©[i], ©[j]) } } return Array(copy.suffix(n)) } }
Swift 3.2和4.x
extension Array { /// Picks `n` random elements (partial Fisher-Yates shuffle approach) subscript (randomPick n: Int) -> [Element] { var copy = self for i in stride(from: count - 1, to: count - n - 1, by: -1) { copy.swapAt(i, Int(arc4random_uniform(UInt32(i + 1)))) } return Array(copy.suffix(n)) } }
用法:
let digits = Array(0...9) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] let pick3digits = digits[randomPick: 3] // [8, 9, 0]
你也可以使用arc4random()来从数组中select三个元素。 像这样的东西:
extension Array { func getRandomElements() -> (T, T, T) { return (self[Int(arc4random()) % Int(count)], self[Int(arc4random()) % Int(count)], self[Int(arc4random()) % Int(count)]) } } let names = ["Peter", "Steve", "Max", "Sandra", "Roman", "Julia"] names.getRandomElements()
这只是一个例子,你也可以在函数中包含逻辑来为每个名称取一个不同的名字。