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


189

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

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

明白な方法はでしょうarray[random index]。しかし、おそらくルビーのようなものがありarray.sampleますか?または、拡張機能を使用してこのようなメソッドを作成できないのですか?


1
まだ別の方法を試しましたか?
フォード県、2014年

私は試みarray[random number from 0 to length-1]ますが、ランダムな整数を迅速に生成する方法を見つけることができません、私がブロックされていなかった場合は、スタックオーバーフローでそれを尋ねます:)ルビーのようなものarray.sample
Fela Winkelmolen 14年

1
Obj-Cと同じようにarc4random()を使用します
Arbitur

質問がJQueryの回答と同じフィードバックを受けなかった理由の説明はありません。ただし、一般に、質問を投稿するときは、これらのガイドラインに従う必要があります。良い質問をするには?。他の人に助けを求める前に、解決策を理解することに少し努力を払ったように見せてください。「choose random number swift」をググると、最初のページはarc4random_uniformを示唆する回答で満たされます。また、RTFD ...「ドキュメントを読む」。この方法でいくつの質問に答えられるかは驚くべきことです。
オースティンA

親切なフィードバックをありがとうございます。ええ、私は自分自身で質問に答えるべきだったと思いますが、他の誰かにほとんど無料の評判ポイントを与えるのは良いことであるように十分に簡単に思えました。そして、公式のAppleの迅速なドキュメントさえ公開されていなかったとき、私はそれを書いた、その時点で確かにGoogleの結果はなかった。しかし、質問はかつて-12だったので、最終的にはプラスになると確信しています:)
Fela Winkelmolen 14

回答:


320

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

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


4
SwiftがIntを返す乱数ジェネレータを提供しないのはなぜですか?この2行目は、ランダムに選択されたIntを返すためだけに非常に冗長に見えます。Intとは対照的に、UInt32を返すことには計算上/構文上の利点がありますか?また、Swiftがなぜこの関数の代わりにIntを提供しないのか、またはユーザーが返してほしい整数のタイプを指定できないのはなぜですか?
オースティンA

メモを追加するために、この乱数ジェネレーターメソッドは、「モジュロバイアス」を防ぐことができます。参照してman arc4randomstackoverflow.com
questions / 10984974

1
@ AustinA、Swift 4.2 DOESには、期待するすべてのスカラーデータ型(Int、Double、Float、UInt32など)に実装されているネイティブの乱数ジェネレーター関数があります。また、値のターゲット範囲を指定できます。とても便利な。Swift 4.2でarray [Int.random(0 .. <array.count)] `を使用できます
Duncan C

Swift 4.2がremoveRandomElement()に加えて機能を実装したいと思いrandomElement()ます。これはでモデル化されremoveFirst()ますが、ランダムインデックスにあるオブジェクトを削除します。
ダンカンC

@DuncanC回避する必要があります0..<array.count(複数の理由により、主な理由は、スライスでは機能しないことと、エラーが発生しやすいことです)。あなたがすることができlet randomIndex = array.indices.randomElement()、続いてlet randomElement = array.remove(at: randomIndex)。にインライン化することもできlet randomElement = array.remove(at: array.indices.randomElement())ます。
アレクサンダー-モニカを復活させる

137

ルーカスが言ったことをリッピングして、次のように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
Swift 2 Tではに名前が変更されましたElement
GDanger

25
空の配列はここでクラッシュを引き起こすことに注意してください
Berik

1
@Berikええと、オプションのElementを返しguard、配列が空かどうかを常に確認してから返すことができますnil
ハリッシュ

1
同意した。アレイは範囲外でクラッシュするため、パフォーマンスが向上します。を呼び出してarc4randomも、パフォーマンスの向上はまったく重要ではありません。答えを更新しました。
Berik 2017

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

}

これはコレクションの範囲外のインデックスでクラッシュする可能性がありますstartIndex != 0
dan

21

スウィフト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

まず、sの静的randomプロパティを実装します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持つsのUnsignedIntegerType場合:

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付きのsの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)
    }
}

...ここunsignedDistanceTounsignedDistanceFromMinplusMinIntMaxヘルパーメソッドは次のように実装できます。

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

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

実際、random()は標準Cライブラリブリッジングからのものであり、ターミナルで「man random」と表示されます。しかし、可用性を指摘してくれてうれしいです!
David H

1
これにより、実行するたびに同じランダムなシーケンスが生成されます
iTSangar

1
@iTSangarあなたが正しい!rand()が正しい使用法です。回答を更新しています。
NatashaTheRobot 2015

6
これは、モジュロバイアスの影響も受けやすくなります。
Aidan Gomez

@matttは、乱数の生成に関する素晴らしい記事を書きました。TL; DR arc4randomファミリーのいずれかがより良い選択です。
elitalon

9

別のSwift 3の提案

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

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

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

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をチェックアウトしてくださいCarthage介してプロジェクトに追加し、上記の例とまったく同じように使用できます。

import HandySwift    

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

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

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

2

スウィフト3

GameKitをインポートする

func getRandomMessage() -> String {

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

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

    return messages[randomNumber].description

}

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

ランダムでをSpriteKit更新するプロジェクトの例を次に示します。SKLabelNodeString

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

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

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

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]

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

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

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

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


1

私はGameKitのGKRandomSource.sharedRandom()を使用するのが最適です。

import GameKit

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

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

または、選択したランダムインデックスでオブジェクトを返すこともできます。関数が最初に文字列を返し、次に配列のインデックスを返すことを確認します。

    return array[randomNumber]

短くて要点。


1

現在、組み込みのメソッドがあり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() }
    }
}

0

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

-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. 文字列の配列を取り、文字列を返すパラメータを持つ関数を宣言しました。

  2. 次に、 ArrayOfStringsを変数に入れます。

  3. 次に、シャッフルされた関数を呼び出し、それを変数に格納します。(4.2でのみサポート)
  4. 次に、文字列の合計数のシャッフルされた値を保存する変数を宣言します。
  5. 最後に、countSのインデックス値でシャッフルされた文字列を返します。

それは基本的に文字列の配列をシャッフルし、合計カウント数のランダムな数のピックを持ち、シャッフルされた配列のランダムなインデックスを返します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.