arrays - Obtenga elementos aleatorios de la matriz en Swift
random shuffle (5)
¿O alguien tiene una solución mejor / más elegante para esto?
Hago.
Algorítmicamente mejor que la respuesta aceptada, que cuenta 1 operaciones
arc4random_uniform
para una
arc4random_uniform
aleatoria completa, simplemente podemos elegir
n
valores en
n
operaciones
arc4random_uniform
.
Y en realidad, obtuve dos formas de hacerlo mejor que la respuesta aceptada:
Mejor solución
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))
}
}
Mejor solución
La siguiente solución es dos veces más rápida que la anterior.
para Swift 3.0 y 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) {
copy.swapAt(i, Int(arc4random_uniform(UInt32(i + 1))))
}
return Array(copy.suffix(n))
}
}
para Swift 3.2 y 4.x
let digits = Array(0...9) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
let pick3digits = digits[randomPick: 3] // [8, 9, 0]
Uso:
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()
Tengo una matriz como:
var names: String = [ "Peter", "Steve", "Max", "Sandra", "Roman", "Julia" ]
Me gustaría obtener 3 elementos aleatorios de esa matriz. Vengo de C # pero rápidamente no estoy seguro de por dónde empezar. Creo que debería barajar la matriz primero y luego elegir los primeros 3 elementos, por ejemplo.
Traté de barajarlo con la siguiente extensión:
extension Array
{
mutating func shuffle()
{
for _ in 0..<10
{
sort { (_,_) in arc4random() < arc4random() }
}
}
}
pero luego dice "''()'' no es convertible a ''[Int]''" en la ubicación de "shuffle ()".
Para elegir una serie de elementos que uso:
var randomPicks = names[0..<4];
que se ve bien hasta ahora.
¿Cómo barajar? ¿O alguien tiene una solución mejor / más elegante para esto?
Puede definir una extensión en Array:
extension Array where Element: Hashable {
func pickUniqueInValue(_ n: Int) -> [Element] {
let set: Set<Element> = Set(self)
guard set.count >= n else {
fatalError("The array has to have at least /(n) unique values")
}
guard n >= 0 else {
fatalError("The number of elements to be picked must be positive")
}
return Array(set.prefix(upTo: set.index(set.startIndex, offsetBy: n)))
}
}
[ "Peter", "Steve", "Max", "Sandra", "Roman", "Julia" ].pickUniqueInValue(3)
Si la matriz inicial puede tener duplicados y desea unicidad en el valor:
extension Array where Element: Hashable { func pickUniqueInValue(_ n: Int) -> [Element] { let set: Set<Element> = Set(self) guard set.count >= n else { fatalError("The array has to have at least /(n) unique values") } guard n >= 0 else { fatalError("The number of elements to be picked must be positive") } return Array(set.prefix(upTo: set.index(set.startIndex, offsetBy: n))) } } [ "Peter", "Steve", "Max", "Sandra", "Roman", "Julia" ].pickUniqueInValue(3)
También puede usar arc4random () para elegir tres elementos de la matriz. Algo como esto:
let playlist = ["Nothing Else Matters", "Stairway to Heaven", "I Want to Break Free", "Yesterday"]
let index = Int(arc4random_uniform(UInt32(playlist.count)))
let song = playlist[index]
Este es solo un ejemplo, también podría incluir lógica en la función para obtener un nombre diferente para cada uno.
Swift 4.1 y abajo
if let song = playlist.randomElement() {
print(song)
} else {
print("Empty playlist.")
}
Swift 4.2 y superior
extension Array {
func pick(_ n: Int) -> [Element] {
guard count >= n else {
fatalError("The count has to be at least /(n)")
}
guard n >= 0 else {
fatalError("The number of elements to be picked must be positive")
}
let shuffledIndices = indices.shuffled().prefix(upTo: n)
return shuffledIndices.map {self[$0]}
}
}
[ "Peter", "Steve", "Max", "Sandra", "Roman", "Julia" ].pick(3)
Xcode 11 • Swift 5.1
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)) }
}
Prueba de patio de recreo
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]
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)) }
}
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] }
}
}