배열에서 임의의 요소를 선택


189

배열이 있고 하나의 요소를 임의로 선택하고 싶다고 가정하십시오.

가장 간단한 방법은 무엇입니까?

확실한 방법은입니다 array[random index]. 그러나 아마도 루비와 같은 것이 array.sample있습니까? 또는 확장을 사용하여 이러한 방법을 만들 수 없다면?


1
아직 다른 방법을 시도해 보셨습니까?
포드 지사

시도해 array[random number from 0 to length-1]보았지만 신속하게 임의의 int를 생성하는 방법을 찾을 수 없습니다. 차단되지 않은 경우 스택 오버플로에 요청할 것입니다. 루비와 같은 것array.sample
펠라 Winkelmolen

1
Obj-C에서와 마찬가지로 arc4random ()을 사용합니다
Arbitur

귀하의 질문이 JQuery와 동일한 피드백을받지 못한 이유에 대한 설명이 없습니다. 그러나 일반적으로 질문을 게시 할 때이 지침을 따라야합니다. 좋은 질문을하는 방법? . 다른 사람에게 도움을 요청하기 전에 해결책을 찾는 데 약간의 노력을 기울인 것처럼 보이게하십시오. Google에서 '임의의 빠른 숫자 선택'을 시작하면 첫 페이지에 arc4random_uniform을 제안하는 답변으로 채워집니다. 또한 RTFD ... "f'ing 문서 읽기". 이런 식으로 몇 개의 질문에 대답 할 수 있는지는 놀랍습니다.
오스틴 A

당신의 친절한 피드백에 감사드립니다. 그렇다, 나는 그 질문에 스스로 대답해야했을 것 같지만, 다른 사람에게 거의 무료 평판을주는 것이 좋을 것 같았다. 그리고 공식 애플 스위프트 문서조차 공개되지 않았을 때, 그 당시에는 구글 결과가 확실히 없었습니다. 그러나 문제는 한 번 -12에 있었으므로 결국 긍정적일 것이라고 확신합니다 :)
Fela Winkelmolen

답변:


321

스위프트 4.2 이상

새로운 권장 방법은 Collection 프로토콜에 내장 된 방법입니다 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)
}

스위프트 4.1 이하

질문에 대답하기 위해 무작위 배열 선택을 달성하기 위해이 작업을 수행 할 수 있습니다.

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

주물은 추악하지만 다른 방법이 없다면 주물이 필요하다고 생각합니다.


4
왜 Swift는 Int를 반환하는 난수 생성기를 제공하지 않습니까? 이 두 번째 줄은 무작위로 선택된 Int를 반환하는 매우 장황한 것 같습니다. Int와 반대로 UInt32를 반환하면 계산 / 구문상의 이점이 있습니까? 또한 Swift 가이 함수에 Int 대안을 제공하지 않거나 사용자가 반환하려는 정수 유형을 지정할 수없는 이유는 무엇입니까?
오스틴 A

메모를 추가하기 위해이 난수 생성기 방법은 "모듈로 바이어스"를 방지 할 수 있습니다. 참조 man arc4randomstackoverflow.com/questions/10984974/…
켄트 리아 우

1
@AustinA, Swift 4.2 DOES에는 Int, Double, Float, UInt32 등 원하는 스칼라 데이터 유형에 구현 된 기본 난수 생성기 기능이 있으며 값의 목표 범위를 제공 할 수 있습니다. 매우 편리합니다. 넌 배열 사용 [Int.random (0 .. <array.count)] '스위프트 4.2
던컨 C

Swift 4.2가 removeRandomElement()에 추가하여 기능을 구현했으면 합니다 randomElement(). 에 모델링 removeFirst()되지만 임의의 인덱스에서 객체를 제거합니다.
Duncan 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

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>

2
신속한 2의 T이름이으로 변경되었습니다 Element.
GDanger

25
비어있는 배열은 여기서 충돌을 일으킨다
Berik

1
@Berik 글쎄, 당신은 선택적인 Element를 반환하고 항상 guard배열이 비어 있는지 확인한 다음 return을 수행 할 수 nil있습니다.
Harish

1
동의했다. 어레이가 범위를 벗어난 상태에서 충돌하여 성능을 발휘할 수 있습니다. 호출 arc4random하면 성능 향상이 완전히 중요하지 않습니다. 답변을 업데이트했습니다.
Berik

45

스위프트 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

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

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

그런 다음 경계 가있는 ClosedIntervals의 경우 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))
    }
}

그런 다음 경계 가있는 ClosedIntervals 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)
    }
}

... 어디에서 unsignedDistanceTo, unsignedDistanceFromMin그리고 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.

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

6
이것은 또한 모듈로 바이어스에 영향을 받기 쉽다.
Aidan Gomez

@mattt는 난수 생성에 관한 훌륭한 기사를 썼습니다 . TL; DR arc4random 제품군 중 하나가 더 나은 선택입니다.
elitalon

9

또 다른 스위프트 3 제안

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

4

다른 사람들을 따라 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()

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

스위프트 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)]

다음은 무작위로 a를 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]

무작위성에 대한 몇 가지 선택 사항이 있습니다. GKRandomSource를 참조하십시오 .

그만큼 GKARC4RandomSource 클래스는 arc4random C 함수 계열에 사용 된 것과 유사한 알고리즘을 사용합니다. 그러나이 클래스의 인스턴스는 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. 그런 다음 shuffled 함수를 호출하여 변수에 저장합니다. (4.2 만 지원)
  4. 그런 다음 문자열의 총 개수를 섞은 값을 저장하는 변수를 선언합니다.
  5. 마지막으로 셔플 된 문자열을 countS의 인덱스 값으로 반환합니다.

기본적으로 문자열 배열을 섞은 다음 총 개수 수의 임의의 수를 선택한 다음 섞인 배열의 임의 인덱스를 반환합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.