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

时间:2014-06-02 20:59:22

标签: arrays swift random

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

最简单的方法是什么?

显而易见的方法是array[random index]。但也许有像ruby的array.sample这样的东西?或者如果没有,可以使用扩展名来创建这样的方法吗?

16 个答案:

答案 0 :(得分:284)

Swift 4.2及以上

新推荐的方法是内置方法:randomElement()。它返回一个可选项,以避免我之前假设的空案例。

let array = ["Frodo", "Sam", "Wise", "Gamgee"]
print(array.randomElement()!) // Using ! knowing I have array.count > 0

如果您没有创建阵列且无法保证计数> 0,你应该做类似的事情:

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

Swift 4.1及以下

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

let array = ["Frodo", "sam", "wise", "gamgee"]
let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
print(array[randomIndex])

铸件很难看,但我相信除非别人有别的办法,否则他们是必须的。

答案 1 :(得分:135)

对卢卡斯说的话,你可以像这样创建一个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>

答案 2 :(得分:45)

Swift 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)))]
    }

}

答案 3 :(得分:21)

Swift 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

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

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

然后,ClosedInterval具有UnsignedIntegerType边界的<{1}}:

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

然后(稍微复杂一点),ClosedInterval的{​​{1}}边界(使用下面进一步描述的辅助方法):

SignedIntegerType

...其中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) } } unsignedDistanceTounsignedDistanceFromMin辅助方法可以按如下方式实施:

plusMinIntMax

最后,对于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] } } 进行一点优化:

Range

答案 4 :(得分:18)

你可以使用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

答案 5 :(得分:9)

另一个Swift 3建议

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

答案 6 :(得分:4)

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

Swift 1.x

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

Swift 2.x

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

答案 7 :(得分:2)

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

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

答案 8 :(得分:2)

以下是<数组的扩展名,其中空数组检查以提高安全性:

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 。您可以将其添加到项目 via Carthage ,然后使用它,就像上面的示例一样:

import HandySwift    

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

此外,它还包括一次选择多个随机元素的选项:

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

答案 9 :(得分:2)

Swift 3

导入GameKit

func getRandomMessage() -> String {

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

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

    return messages[randomNumber].description

}

答案 10 :(得分:2)

Swift 3 - 简单易用。

  1. 创建数组

    var arrayOfColors = [UIColor.red, UIColor.yellow, UIColor.orange, UIColor.green]
    
  2. 创建随机颜色

    let randomColor = arc4random() % UInt32(arrayOfColors.count)
    
  3. 将该颜色设置为您的对象

    your item = arrayOfColors[Int(randomColor)]
    
  4. 以下是使用随机SpriteKit更新SKLabelNode的{​​{1}}项目的示例:

    String

答案 11 :(得分:2)

如果您希望能够通过无重复项从阵列中获取多个随机元素,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类慢,但更随机。使用此源时,重要的是您使用随机数字不会显示重复模式,而性能则不那么重要。

答案 12 :(得分:1)

我发现使用GameKit的GKRandomSource.sharedRandom()最适合我。

import GameKit

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

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

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

    return array[randomNumber]

简短而重要。

答案 13 :(得分:0)

最新的swift3代码试试它的工作正常

imgprocessor.py

答案 14 :(得分:0)

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

let foods = ["?", "?", "?", "?"]
let myDinner = foods.randomElement()

如果您要从一个集合中提取最多n个随机元素,则可以添加如下扩展名:

extension Collection {
    func randomUniqueElements(_ 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() }
    }
}

答案 15 :(得分:-2)

我想出了使用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. 我们声明了一个带有参数的函数,该参数带有一个String数组并返回一个String。

  2. 然后我们将 ArrayOfStrings 放入变量中。

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

基本上是对字符串数组进行混洗,然后从总数中随机选择一个数字,然后返回经过混洗的数组的随机索引。