web-dev-qa-db-ja.com

配列からランダムな要素を選ぶ

配列があり、1つの要素をランダムに選びたいとします。

これを行う最も簡単な方法は何でしょうか。

明らかな方法はarray[random index]です。しかし、おそらくRubyのarray.sampleのようなものがあるのでしょうか。それとも、そのようなメソッドを拡張子を使って作成できないのでしょうか。

169

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>
136
Phae Deepsky

Swift 4.2以上

新しく推奨される方法は組み込みメソッドrandomElement()です。これは私が以前に想定した空のケースを避けるためにオプションを返します。

let array = ["Frodo", "Sam", "Wise", "Gamgee"]
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", "sam", "wise", "gamgee"]
let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
print(array[randomIndex])

キャスティングは醜いです、しかし私は彼らが他の誰かが別の方法を持っていない限り必要であると思います。

287
Lucas Derraugh

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

}
45
Andrey Gordeev

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

まず、randomsに静的な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を境界とするUnsignedIntegerTypesの場合、

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の境界を持つSignedIntegerTypesに対して(以下で説明するヘルパーメソッドを使用して):

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

...これは整数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
    }
}
21
milos

拡張機能には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
18
NatashaTheRobot

もう1つのSwift 3提案

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

他の人が以下のように答えますが、Swift 2をサポートします。

スイフト1.x

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

スイフト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()
4
Aidan Gomez

重複なしで配列から複数のランダム要素を取り出すことができるようにしたい場合は、GameplayKitでカバーしています。

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

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

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

ランダム性については2つの選択肢があります。 GKRandomSource を参照してください。

GKARC4RandomSourceクラスはarc4randomファミリーのC関数で採用されているものと同様のアルゴリズムを使用します。 (ただし、このクラスのインスタンスはarc4random関数の呼び出しからは独立しています。)

GKLinearCongruentialRandomSourceクラスは、GKARC4RandomSourceクラスより高速ですがランダムではないアルゴリズムを使用します。 (特に、生成された数値の下位ビットは上位ビットよりも頻繁に繰り返されます。)パフォーマンスが堅牢な予測不可能性よりも重要な場合は、このソースを使用してください。

GKMersenneTwisterRandomSourceクラスは、GKARC4RandomSourceクラスより低速ですがランダムなアルゴリズムを使用します。乱数を使用しても繰り返しパターンが表示されず、パフォーマンスがそれほど問題にならないことが重要な場合は、この情報源を使用してください。

2
bcattle

スイフト3

gameKitをインポートする

func getRandomMessage() -> String {

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

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

    return messages[randomNumber].description

}
2
Dazzle

空の配列をチェックする代替の機能的実装。

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
Evgenii

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

これは、ランダムなSpriteKitSKLabelNodeを更新するStringプロジェクトの例です。

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

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

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

これは配列の拡張で、安全性を高めるために空の配列チェックを付けたものです。

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

Frameworkをお望みであれば、さらに便利な機能もあるので、チェックアウトHandySwift。あなたはそれをあなたのプロジェクトに追加することができますvia Carthageそして上の例のようにそれを使います:

import HandySwift    

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

さらに、一度に複数のランダムな要素を取得するオプションも含まれています。

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

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() }
    }
}
1
Gigisommo

私は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]

手短に言うとポイント。

1
djames04

最新の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)])