Bir diziden rastgele bir öğe seçme


189

Diyelim ki bir dizim var ve rastgele bir öğe seçmek istiyorum.

Bunu yapmanın en basit yolu ne olurdu?

Açık olan yol olurdu array[random index]. Ama belki de yakut gibi bir şey var array.samplemı? Yoksa böyle bir yöntem bir uzantı kullanılarak oluşturulabilir mi?


1
Farklı yöntemler denediniz mi?
ford vali

Denerdim array[random number from 0 to length-1], ama hızlı bir şekilde rastgele bir int üretmeyi bulamıyorum, bloke olmasaydım yığın taşmasıyla ilgili soru sorardım :) Belki de oradayken soruyu yarım çözümlerle kirletmek istemedim ruby gibi bir şeyarray.sample
Fela Winkelmolen

1
Obj-C
Arbitur

Sorunuzun neden JQuery muadili ile aynı geri bildirimi almadığına dair bir açıklama yok. Ancak genel olarak, bir soru gönderirken bu yönergeleri izlemelisiniz. İyi bir soru nasıl sorulur? . Başka birinden yardım istemeden önce bir çözüm bulmak için biraz çaba harcadığınızdan emin olun. Google "rastgele sayı swift seçin", ilk sayfa arc4random_uniform öneren cevaplarla doldurulur. Ayrıca, RTFD ... "belge belgelerini okuyun". Bu şekilde kaç soru cevaplanabileceği şaşırtıcı.
Austin A

Nazik geri bildiriminiz için teşekkür ederiz. Evet, sanırım soruyu kendim cevaplamalıydım, ama başka birine neredeyse ücretsiz itibar puanları vermenin güzel olduğu yeterince kolay görünüyordu. Resmi Apple hızlı dokümanları bile herkese açık olmadığında yazdım, o sırada kesin bir Google sonucu yoktu. Ama soru bir zamanlar -12 idi, bu yüzden sonunda olumlu olacağından oldukça eminiz :)
Fela Winkelmolen

Yanıtlar:


321

Swift 4.2 ve üstü

Yeni önerilen yaklaşımdır yerleşik bir Koleksiyonu protokolü üzerinde yöntemle: randomElement(). Daha önce karşıladığım boş vakayı önlemek için isteğe bağlı olarak döner.

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

Diziyi oluşturmazsanız ve sayısı> 0 olarak garanti edilmezse, şöyle bir şey yapmalısınız:

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

Swift 4.1 ve altı

Sadece sorunuzu cevaplamak için, rastgele dizi seçimi elde etmek için bunu yapabilirsiniz:

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

Dökümler çirkin, ama başka birinin başka bir yolu yoksa gerekli olduklarına inanıyorum.


4
Swift neden Int döndüren rasgele bir sayı üreteci sunmuyor? Bu 2. satır sadece rastgele seçilen Int dönmek için çok ayrıntılı görünüyor. Int yerine UInt32 döndürmenin bazı hesaplama / sözdizimsel avantajı var mı? Ayrıca, Swift neden bu işleve bir Int alternatifi sunmuyor veya bir kullanıcının ne tür bir tamsayı döndürmesini istemesine izin vermiyor?
Austin A

Bir not eklemek için, bu rasgele sayı üreteci yöntemi "modulo sapması" nı engelleyebilir. Bakınız man arc4randomve stackoverflow.com/questions/10984974/…
Kent Liau

1
@AustinA, Swift 4.2 DOES, Double, Float, UInt32, vb. Beklediğiniz tüm skaler veri türlerinde uygulanan yerel rasgele sayı üreteci işlevine sahiptir. Ve değerler için hedef aralıklar sağlamanızı sağlar. Çok kullanışlı. Swift 4.2
Duncan C

Keşke Swift 4.2 removeRandomElement()ek olarak bir fonksiyon uygulamış randomElement(). Modellenir removeFirst(), ancak bir nesneyi rastgele bir dizinde kaldırır.
Duncan C

@DuncanC Kaçınılmalıdır 0..<array.count(birden fazla nedenden dolayı, ana olanlar dilimler için çalışmadığı ve hata eğilimli olması). Yapabilirsiniz let randomIndex = array.indices.randomElement(), ardından let randomElement = array.remove(at: randomIndex). Hatta satır içi bile yapabilirsiniz let randomElement = array.remove(at: array.indices.randomElement()).
Alexander - Monica'yı eski

137

Lucas'ın söylediklerine dayanarak, Array sınıfına şu şekilde bir uzantı oluşturabilirsiniz:

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

Örneğin:

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
Hızlı 2 Tolarak yeniden adlandırıldı Element.
GDanger

25
Boş bir dizinin burada
çökmeye

1
@Berik Peki, isteğe bağlı bir Öğe döndürebilir ve sonra guarddizinin boş olup olmadığını görmek için her zaman bir kontrol yapabilir ve sonra geri dönebilirsiniz nil.
Harish

1
Kabul. Diziler, performanslarının gösterilebilmesi için sınırların dışında çöküyor. Çağrı arc4randomyapmak herhangi bir performans kazanımını önemsiz kılar. Cevabı güncelledim.
Berik

45

Swift 4 sürümü:

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

}

Bu, koleksiyonlardaki sınırların dışında bir dizinle çökebilirstartIndex != 0
dan

21

In Swift 2.2 bu elimizdeki böylece genelleştirilebilir:

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

İlk olarak, s randomiçin statik özellik uygulamak 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)
    }
}

Sonra, sınırları olan ClosedIntervals için 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))
    }
}

Sonra (biraz daha dahil), sınırları olan ClosedIntervals için SignedIntegerType(aşağıda daha ayrıntılı olarak açıklanan yardımcı yöntemleri kullanarak):

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

... nerede unsignedDistanceTo, unsignedDistanceFromMinve plusMinIntMaxaşağıdaki gibi yardımcı yöntemler uygulanabilir:

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

Son olarak, tüm koleksiyonlar için 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]
    }
}

... tamsayılar için biraz optimize edilebilir Range:

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

Uzantı için Swift'in yerleşik random () işlevini de kullanabilirsiniz:

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

Aslında random () Standart C kütüphanesi köprüleme, onu ve Terminal, "adam rastgele" görebilirsiniz. Ama kullanılabilirliği işaret sevindim!
David H

1
Bu, her çalıştırıldığında aynı rastgele diziyi üretir
iTSangar

1
@iTSangar haklısın! rand () kullanılacak doğru olanıdır. Cevabım güncelleniyor.
NatashaTheRobot

6
Bu aynı zamanda modulo yanlılığına da duyarlıdır.
Aidan Gomez

@mattt rastgele sayılar üretmeyle ilgili güzel bir makale yazdı . TL; arc4random ailesinin herhangi birinden DR daha iyi bir seçimdir.
elitalon

9

Başka bir Swift 3 önerisi

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

4

Diğerlerini cevaplamak ama Swift 2 desteği ile.

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

Örneğin:

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

2

Boş dizi kontrolü ile alternatif bir fonksiyonel uygulama.

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

Daha fazla güvenlik için boş bir dizi kontrolüne sahip Dizilerde bir uzantı :

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

Bu kadar basit kullanabilirsiniz :

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

Bazı kullanışlı özellikleri de olan bir Çerçeveyi tercih ediyorsanız , HandySwift'e göz atın . Bunu Carthage aracılığıyla projenize ekleyebilir ve daha sonra yukarıdaki örnekte olduğu gibi kullanabilirsiniz:

import HandySwift    

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

Ayrıca, aynı anda birden fazla rastgele öğe alma seçeneği de içerir :

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

2

Hızlı 3

GameKit'i içe aktar

func getRandomMessage() -> String {

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

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

    return messages[randomNumber].description

}

2

Swift 3 - kullanımı kolay basit.

  1. Dizi Oluştur

    var arrayOfColors = [UIColor.red, UIColor.yellow, UIColor.orange, UIColor.green]
  2. Rastgele Renk Oluştur

    let randomColor = arc4random() % UInt32(arrayOfColors.count)
  3. Bu rengi nesnenize ayarlayın

    your item = arrayOfColors[Int(randomColor)]

İşte bir rastgele SpriteKitgüncellenen bir proje örneği :SKLabelNodeString

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

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

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

2

Çoğaltmadan dizinizden birden fazla rastgele öğe elde etmek istiyorsanız , GameplayKit size şunları sunar:

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

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

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

Rasgelelik için birkaç seçeneğiniz var, bkz. GKRandomSource :

GKARC4RandomSourceC sınıfı fonksiyonlarının arc4random ailesinde kullanılana benzer bir algoritma kullanır. (Ancak, bu sınıfın örnekleri arc4random işlevlerine yapılan çağrılardan bağımsızdır.)

GKLinearCongruentialRandomSourceSınıf GKARC4RandomSource sınıfından daha bir hızlı algoritma, ancak daha az rastgele kullanır. (Özellikle, üretilen sayıların düşük bitleri yüksek bitlerden daha sık yinelenir.) Performans sağlam öngörülemezlikten daha önemli olduğunda bu kaynağı kullanın.

GKMersenneTwisterRandomSourceSınıf GKARC4RandomSource sınıfı, daha yavaş fakat daha rastgele bir algoritma kullanır. Rastgele sayılar kullanmanın yinelenen kalıplar göstermemesi ve performansın daha az endişe etmesi önemli olduğunda bu kaynağı kullanın.


1

GameKit'in GKRandomSource.sharedRandom () kullanarak benim için en iyi sonucu buluyorum.

import GameKit

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

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

veya nesneyi seçilen rastgele dizine döndürebilirsiniz. Önce işlevin bir String döndürdüğünden emin olun ve ardından dizinin dizinini döndürün.

    return array[randomNumber]

Kısa ve öz.


1

Şu anda yerleşik bir yöntem var Collection:

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

nBir koleksiyondan rastgele öğelere kadar çıkarmak istiyorsanız , bunun gibi bir uzantı ekleyebilirsiniz:

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

Ve benzersiz olmalarını istiyorsanız, a kullanabilirsiniz Set, ancak koleksiyonun öğeleri Hashableprotokole uymalıdır :

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

0

En son swift3 kodu iyi çalışıyor deneyin

 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'de sunulan yeni özellikleri kullanarak bunu yapmanın çok farklı bir yolunu buldum.

// 👇🏼 - 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. Dizeler dizisini alan ve bir String döndüren parametrelerle bir işlev ilan ettik.

  2. Sonra bir değişken içinde ArrayOfStrings alırız .

  3. Sonra karıştırılmış işlevi çağırırız ve bunu bir değişkende saklarız. (Yalnızca 4.2'de desteklenir)
  4. Ardından, Dize'nin toplam sayısının karıştırılmış bir değerini kaydeden bir değişken beyan ederiz.
  5. Son olarak karıştırılan dizeyi countS dizin değerine döndürüyoruz.

Temelde dizeler dizisini karıştırır ve ardından toplam sayım sayısının rastgele bir seçimine sahiptir ve daha sonra karıştırılan dizinin rastgele dizinini döndürür.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.