从数组中随机选择一个元素

假设我有一个数组,我想随机选择一个元素。

最简单的方法是什么?

最明显的方法是array[random index]。但也许有一些像ruby的array.sample?如果没有,可以通过使用扩展来创建这样的方法吗?

116256 次浏览

Swift 4.2及以上版本

新的推荐方法是Collection协议上的内置方法:randomElement()。它返回一个可选选项,以避免前面假设的空情况。

let array = ["Frodo", "Samwise", "Merry", "Pippin"]
print(array.randomElement()!) // Using ! knowing I have array.count > 0

如果你没有创建数组并且不能保证count >0,你应该这样做:

if let randomElement = array.randomElement() {
print(randomElement)
}

Swift 4.1及以下版本

为了回答你的问题,你可以这样做来实现随机数组选择:

let array = ["Frodo", "Samwise", "Merry", "Pippin"]
let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
print(array[randomIndex])

演员阵容很难看,但我相信他们是必须的,除非有人有别的办法。

重复Lucas所说的,你可以像这样创建Array类的扩展:

extension Array {
func randomItem() -> Element? {
if isEmpty { return nil }
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}

例如:

let myArray = [1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16]
let myItem = myArray.randomItem() // Note: myItem is an Optional<Int>

你可以使用Swift内置的random()函数以及扩展:

extension Array {
func sample() -> Element {
let randomIndex = Int(rand()) % count
return self[randomIndex]
}
}


let array = [1, 2, 3, 4]


array.sample() // 2
array.sample() // 2
array.sample() // 3
array.sample() // 3


array.sample() // 1
array.sample() // 1
array.sample() // 3
array.sample() // 1

检查空数组的替代功能实现。

func randomArrayItem<T>(array: [T]) -> T? {
if array.isEmpty { return nil }
let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
return array[randomIndex]
}


randomArrayItem([1,2,3])

斯威夫特2.2中,这可以泛化,这样我们就有:

UInt.random
UInt8.random
UInt16.random
UInt32.random
UInt64.random
UIntMax.random


// closed intervals:


(-3...3).random
(Int.min...Int.max).random


// and collections, which return optionals since they can be empty:


(1..<4).sample
[1,2,3].sample
"abc".characters.sample
["a": 1, "b": 2, "c": 3].sample

首先,为UnsignedIntegerTypes实现静态random属性:

import Darwin


func sizeof <T> (_: () -> T) -> Int { // sizeof return type without calling
return sizeof(T.self)
}


let ARC4Foot: Int = sizeof(arc4random)


extension UnsignedIntegerType {
static var max: Self { // sadly `max` is not required by the protocol
return ~0
}
static var random: Self {
let foot = sizeof(Self)
guard foot > ARC4Foot else {
return numericCast(arc4random() & numericCast(max))
}
var r = UIntMax(arc4random())
for i in 1..<(foot / ARC4Foot) {
r |= UIntMax(arc4random()) << UIntMax(8 * ARC4Foot * i)
}
return numericCast(r)
}
}

然后,对于具有UnsignedIntegerType边界的__abc0:

extension ClosedInterval where Bound : UnsignedIntegerType {
var random: Bound {
guard start > 0 || end < Bound.max else { return Bound.random }
return start + (Bound.random % (end - start + 1))
}
}

然后(稍微复杂一点),对于具有SignedIntegerType边界的__abc0(使用下面进一步描述的辅助方法):

extension ClosedInterval where Bound : SignedIntegerType {
var random: Bound {
let foot = sizeof(Bound)
let distance = start.unsignedDistanceTo(end)
guard foot > 4 else { // optimisation: use UInt32.random if sufficient
let off: UInt32
if distance < numericCast(UInt32.max) {
off = UInt32.random % numericCast(distance + 1)
} else {
off = UInt32.random
}
return numericCast(start.toIntMax() + numericCast(off))
}
guard distance < UIntMax.max else {
return numericCast(IntMax(bitPattern: UIntMax.random))
}
let off = UIntMax.random % (distance + 1)
let x = (off + start.unsignedDistanceFromMin).plusMinIntMax
return numericCast(x)
}
}

... 其中unsignedDistanceTounsignedDistanceFromMinplusMinIntMax helper方法可以如下方式实现:

extension SignedIntegerType {
func unsignedDistanceTo(other: Self) -> UIntMax {
let _self = self.toIntMax()
let other = other.toIntMax()
let (start, end) = _self < other ? (_self, other) : (other, _self)
if start == IntMax.min && end == IntMax.max {
return UIntMax.max
}
if start < 0 && end >= 0 {
let s = start == IntMax.min ? UIntMax(Int.max) + 1 : UIntMax(-start)
return s + UIntMax(end)
}
return UIntMax(end - start)
}
var unsignedDistanceFromMin: UIntMax {
return IntMax.min.unsignedDistanceTo(self.toIntMax())
}
}


extension UIntMax {
var plusMinIntMax: IntMax {
if self > UIntMax(IntMax.max) { return IntMax(self - UIntMax(IntMax.max) - 1) }
else { return IntMax.min + IntMax(self) }
}
}

最后,对于Index.Distance == Int所在的所有集合:

extension CollectionType where Index.Distance == Int {
var sample: Generator.Element? {
if isEmpty { return nil }
let end = UInt(count) - 1
let add = (0...end).random
let idx = startIndex.advancedBy(Int(add))
return self[idx]
}
}

... 可以对整数Ranges进行一些优化:

extension Range where Element : SignedIntegerType {
var sample: Element? {
guard startIndex < endIndex else { return nil }
let i: ClosedInterval = startIndex...endIndex.predecessor()
return i.random
}
}


extension Range where Element : UnsignedIntegerType {
var sample: Element? {
guard startIndex < endIndex else { return nil }
let i: ClosedInterval = startIndex...endIndex.predecessor()
return i.random
}
}

跟随其他人的回答,但有Swift 2支持。

快1.倍

extension Array {
func sample() -> T {
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}

快2.倍

extension Array {
func sample() -> Element {
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}

例如:

let arr = [2, 3, 5, 7, 9, 11, 13, 17, 19, 23, 29, 31]
let randomSample = arr.sample()

为了更加安全,这里有一个数组扩展空数组检查:

extension Array {
func sample() -> Element? {
if self.isEmpty { return nil }
let randomInt = Int(arc4random_uniform(UInt32(self.count)))
return self[randomInt]
}
}

你可以使用就这么简单:

let digits = Array(0...9)
digits.sample() // => 6

如果你更喜欢框架,它也有一些更方便的功能,那么签出HandySwift。你可以把它添加到你的项目通过迦太基中,然后像上面的例子一样使用它:

import HandySwift


let digits = Array(0...9)
digits.sample() // => 8

此外,它还包括一个获取同时出现多个随机元素的选项:

digits.sample(size: 3) // => [8, 0, 7]

我发现使用GameKit的gkrandomsource . sharerandom()最适合我。

import GameKit


let array = ["random1", "random2", "random3"]


func getRandomIndex() -> Int {
let randomNumber = GKRandomSource.sharedRandom().nextIntWithUpperBound(array.count)
return randomNumber

或者可以返回所选随机下标处的对象。确保函数首先返回String,然后返回数组的索引。

    return array[randomNumber]

言简意赅。

斯威夫特4版本:

extension Collection where Index == Int {


/**
Picks a random element of the collection.


- returns: A random element of the collection.
*/
func randomElement() -> Iterator.Element? {
return isEmpty ? nil : self[Int(arc4random_uniform(UInt32(endIndex)))]
}


}

Swift的另一个建议

private extension Array {
var randomElement: Element {
let index = Int(arc4random_uniform(UInt32(count)))
return self[index]
}
}

斯威夫特3

进口GameKit

func getRandomMessage() -> String {


let messages = ["one", "two", "three"]


let randomNumber = GKRandomSource.sharedRandom().nextInt(upperBound: messages.count)


return messages[randomNumber].description


}

最新swift3代码尝试它的工作良好

 let imagesArray = ["image1.png","image2.png","image3.png","image4.png"]


var randomNum: UInt32 = 0
randomNum = arc4random_uniform(UInt32(imagesArray.count))
wheelBackgroundImageView.image = UIImage(named: imagesArray[Int(randomNum)])

Swift 3 -简单易用。

  1. < p >创建数组

    var arrayOfColors = [UIColor.red, UIColor.yellow, UIColor.orange, UIColor.green]
    
  2. Create Random Color

    let randomColor = arc4random() % UInt32(arrayOfColors.count)
    
  3. Set that color to your object

    your item = arrayOfColors[Int(randomColor)]
    

Here is an example from a SpriteKit project updating a SKLabelNode with a random String:

    let array = ["one","two","three","four","five"]


let randomNumber = arc4random() % UInt32(array.count)


let labelNode = SKLabelNode(text: array[Int(randomNumber)])

如果你想用没有重复的从数组中获得多个随机元素,GameplayKit已经覆盖了:

import GameplayKit
let array = ["one", "two", "three", "four"]


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


let firstRandom = shuffled[0]
let secondRandom = shuffled[1]

对于随机性,你有几个选择,参见GKRandomSource:

GKARC4RandomSource类使用的算法类似于arc4random系列C函数中的算法。(然而,该类的实例独立于对arc4random函数的调用。)

GKLinearCongruentialRandomSource类使用的算法比GKARC4RandomSource类更快,但没有那么随机。(具体来说,生成的数字的低位比高位更经常重复。)当性能比健壮的不可预测性更重要时,请使用此来源。

GKMersenneTwisterRandomSource类使用的算法比GKARC4RandomSource类更慢,但更随机。当您使用的随机数不显示重复模式且性能不太受关注时,请使用此源。

我想出了一个非常不同的方法来做到这一点,使用Swift 4.2中引入的新功能。

// 👇🏼 - 1
public func shufflePrintArray(ArrayOfStrings: [String]) -> String {
// - 2
let strings = ArrayOfStrings
//- 3
var stringans =  strings.shuffled()
// - 4
var countS = Int.random(in: 0..<strings.count)
// - 5
return stringans[countS]
}




  1. 我们声明了一个带有参数的函数,它接受一个字符串数组并返回一个字符串。

  2. 然后我们在变量中取ArrayOfStrings

  3. 然后我们调用洗牌后的函数并将其存储在一个变量中。(仅在4.2中支持)
  4. 然后声明一个变量,该变量保存String的总计数的洗牌值。
  5. 最后,我们返回经过洗牌的字符串在countS的索引值处。

它基本上是对字符串数组进行洗牌然后从count的总数中随机抽取一个数字然后返回洗牌后数组的随机下标。

现在在Collection上有一个内置方法:

let foods = ["🍕", "🍔", "🍣", "🍝"]
let myDinner = foods.randomElement()

如果你想从一个集合中提取最多n的随机元素,你可以添加一个像这样的扩展:

extension Collection {
func randomElements(_ count: Int) -> [Element] {
var shuffledIterator = shuffled().makeIterator()
return (0..<count).compactMap { _ in shuffledIterator.next() }
}
}

如果你想要它们是唯一的,你可以使用Set,但集合的元素必须符合Hashable协议:

extension Collection where Element: Hashable {
func randomUniqueElements(_ count: Int) -> [Element] {
var shuffledIterator = Set(shuffled()).makeIterator()
return (0..<count).compactMap { _ in shuffledIterator.next() }
}
}