在Swift中从数组中获取随机元素

hyrbngr7  于 2023-02-03  发布在  Swift
关注(0)|答案(6)|浏览(168)

我有一个数组:

var names: String = [ "Peter", "Steve", "Max", "Sandra", "Roman", "Julia" ]

我想从这个数组中得到3个随机元素。我来自C#,但在swift中我不确定从哪里开始。我想我应该先 Shuffle 数组,然后从中挑选前3个元素,例如?
我试着用下面的扩展名来打乱它:

extension Array
{
    mutating func shuffle()
    {
        for _ in 0..<10
        {
            sort { (_,_) in arc4random() < arc4random() }
        }
    }
}

但是它接着说"'()'不能在" shuffle()"的位置转换为"[Int]'"。
为了挑选一些元素,我使用:

var randomPicks = names[0..<4];

目前看来还不错。
如何 Shuffle ?或者有没有人有更好/更优雅的解决方案?

d7v8vwbk

d7v8vwbk1#

超码11 ·斯威夫特5.1

extension Collection {
    func choose(_ n: Int) -> ArraySlice<Element> { shuffled().prefix(n) }
}

Playground测试
x一个一个一个一个x一个一个二个一个x一个一个三个一个

vsdwdz23

vsdwdz232#

或者有人有更好/更优雅的解决方案吗?
是的,算法上比公认的答案更好,它计数-1arc4random_uniform次全 Shuffle 操作,我们可以简单地在 * n * arc4random_uniform次操作中选择 * 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] }
    }
}

最佳解决方案

下面的解决方案比前面的快两倍。

适用于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(&copy[i], &copy[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]
icomxhvb

icomxhvb3#

您可以在Array上定义扩展:

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)

如果初始数组可能有重复项,并且您希望值具有唯一性:

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

lnlaulya4#

Swift 4.1及更低版本

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]

Swift 4.2及更高版本

if let song = playlist.randomElement() {
  print(song)
} else {
  print("Empty playlist.")
}
2skhul33

2skhul335#

你也可以使用arc4random()从数组中只选择三个元素,如下所示:

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

这仅仅是一个例子,你也可以在函数中包含逻辑来为每一个函数获得不同的名称。

e5nqia27

e5nqia276#

您可以使用shuffle()方法并选取混洗数组的前3项,以从原始数组中获取3个随机元素:

超码14 ·斯威夫特5.7

var names: String = [ "Peter", "Steve", "Max", "Sandra", "Roman", "Julia" ]
    let shuffledNameArray = names.shuffled()
    let randomNames = Array(shuffledNameArray.prefix(3))
    print(randomNames)

相关问题