Apple의 Swift 언어에서 난수를 어떻게 생성합니까?
Swift 책이 난수 생성기의 구현을 제공한다는 것을 알고 있습니다. 이 구현을 자신의 프로그램에 복사하여 붙여 넣는 것이 가장 좋은 방법입니까? 아니면 지금 사용할 수있는 라이브러리가 있습니까?
스위프트 4.2 이상
Xcode 10과 함께 제공되는 Swift 4.2에는 다양한 데이터 유형에 사용하기 쉬운 새로운 임의 기능이 도입되었습니다. random()
숫자 유형 에서 메소드를 호출 할 수 있습니다 .
let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()
arc4random_uniform(n)
0과 n-1 사이의 임의의 정수에 사용하십시오 .
let diceRoll = Int(arc4random_uniform(6) + 1)
결과를 Int로 캐스팅하면 var를 명시 적으로 입력 할 필요가 없습니다 UInt32
(Swifty가 아닌 것처럼 보입니다).
편집 : Swift 3.0 업데이트
arc4random
Swift에서는 잘 작동하지만 기본 기능은 32 비트 정수 유형 ( Int
iPhone 5S 및 최신 Mac에서는 64 비트)으로 제한됩니다. 정수 리터럴로 표현할 수있는 임의의 숫자 유형에 대한 일반 함수는 다음과 같습니다.
public func arc4random<T: ExpressibleByIntegerLiteral>(_ type: T.Type) -> T {
var r: T = 0
arc4random_buf(&r, MemoryLayout<T>.size)
return r
}
이 새로운 일반 함수를 사용하여 확장 UInt64
하고 경계 인수를 추가하고 모듈러스 바이어스를 완화 할 수 있습니다 . (이것은 arc4random.c 에서 똑바로 들어 올려집니다 )
public extension UInt64 {
public static func random(lower: UInt64 = min, upper: UInt64 = max) -> UInt64 {
var m: UInt64
let u = upper - lower
var r = arc4random(UInt64.self)
if u > UInt64(Int64.max) {
m = 1 + ~u
} else {
m = ((max - (u * 2)) + 1) % u
}
while r < m {
r = arc4random(UInt64.self)
}
return (r % u) + lower
}
}
이를 통해 우리는 Int64
오버플로를 처리하면서 동일한 인수로 확장 할 수 있습니다 .
public extension Int64 {
public static func random(lower: Int64 = min, upper: Int64 = max) -> Int64 {
let (s, overflow) = Int64.subtractWithOverflow(upper, lower)
let u = overflow ? UInt64.max - UInt64(~s) : UInt64(s)
let r = UInt64.random(upper: u)
if r > UInt64(Int64.max) {
return Int64(r - (UInt64(~lower) + 1))
} else {
return Int64(r) + lower
}
}
}
가족을 완성하려면 ...
private let _wordSize = __WORDSIZE
public extension UInt32 {
public static func random(lower: UInt32 = min, upper: UInt32 = max) -> UInt32 {
return arc4random_uniform(upper - lower) + lower
}
}
public extension Int32 {
public static func random(lower: Int32 = min, upper: Int32 = max) -> Int32 {
let r = arc4random_uniform(UInt32(Int64(upper) - Int64(lower)))
return Int32(Int64(r) + Int64(lower))
}
}
public extension UInt {
public static func random(lower: UInt = min, upper: UInt = max) -> UInt {
switch (_wordSize) {
case 32: return UInt(UInt32.random(UInt32(lower), upper: UInt32(upper)))
case 64: return UInt(UInt64.random(UInt64(lower), upper: UInt64(upper)))
default: return lower
}
}
}
public extension Int {
public static func random(lower: Int = min, upper: Int = max) -> Int {
switch (_wordSize) {
case 32: return Int(Int32.random(Int32(lower), upper: Int32(upper)))
case 64: return Int(Int64.random(Int64(lower), upper: Int64(upper)))
default: return lower
}
}
}
결국, 우리는 마침내 다음과 같이 할 수 있습니다 :
let diceRoll = UInt64.random(lower: 1, upper: 7)
스위프트 4.2 편집
가져온 C 함수 arc4random_uniform ()을 사용하는 대신 Swift 4.2부터 Swift 고유의 고유 함수를 사용할 수 있습니다.
// Generates integers starting with 0 up to, and including, 10
Int.random(in: 0 ... 10)
random(in:)
다른 기본 값에 대한 임의의 값을 얻는 데 사용할 수도 있습니다 . Int, Double, Float 및 Bool과 같은
스위프트 버전 <4.2
이 방법은 Int
주어진 최소값과 최대 값 사이의 임의의 값 을 생성합니다
func randomInt(min: Int, max: Int) -> Int {
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
나는이 코드를 사용했다 :
var k: Int = random() % 10;
iOS 9부터 새로운 GameplayKit 클래스를 사용하여 다양한 방법으로 난수를 생성 할 수 있습니다.
선택할 수있는 네 가지 소스 유형이 있습니다 : 일반 무작위 소스 (이름이없는 시스템, 그 아래에있는 것을 선택하는 시스템), 선형 합동, ARC4 및 Mersenne Twister. 이들은 임의의 정수, 부동 및 부울을 생성 할 수 있습니다.
가장 간단한 수준에서 다음과 같이 시스템의 내장 임의 소스에서 난수를 생성 할 수 있습니다.
GKRandomSource.sharedRandom().nextInt()
이는 -2,147,483,648과 2,147,483,647 사이의 숫자를 생성합니다. 0과 상한 (제외) 사이의 숫자를 원하면 다음을 사용하십시오.
GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
GameplayKit에는 주사위와 함께 사용할 수있는 편리한 생성자가 있습니다. 예를 들어 다음과 같이 6 면체 주사위를 굴릴 수 있습니다.
let d6 = GKRandomDistribution.d6()
d6.nextInt()
또한 GKShuffledDistribution과 같은 것을 사용하여 무작위 분포를 형성 할 수 있습니다. 좀 더 설명이 필요하지만 관심이 있다면 GameplayKit 난수에 대한 튜토리얼을 읽을 수 있습니다 .
C에서와 동일한 방식으로 수행 할 수 있습니다.
let randomNumber = arc4random()
randomNumber
유형 UInt32
(32 비트 부호없는 정수) 인 것으로 추론됩니다.
사용하다 arc4random_uniform()
용법:
arc4random_uniform(someNumber: UInt32) -> UInt32
이 범위에서 당신에게 임의의 정수를 제공 0
하는 someNumber - 1
.
의 최대 값 UInt32
은 4,294,967,295 (즉 2^32 - 1
)입니다.
예 :
동전 던지기
let flip = arc4random_uniform(2) // 0 or 1
주사위 롤
let roll = arc4random_uniform(6) + 1 // 1...6
10 월의 임의의 날
let day = arc4random_uniform(31) + 1 // 1...31
1990 년대의 무작위 연도
let year = 1990 + arc4random_uniform(10)
일반적인 형태 :
let number = min + arc4random_uniform(max - min + 1)
어디에 number
, max
그리고 min
있습니다 UInt32
.
는 어때...
arc4random ()
0을 사용하여 2 ^ 32-1 arc4random()
을 생성 하는 난수를 얻을 수도 있습니다 UInt32
. 따라서 0
와 사이의 임의의 숫자를 얻으려면 숫자를 x-1
나누고 x
나머지를 취할 수 있습니다 . 즉, 나머지 연산자 (%)를 사용하십시오 .
let number = arc4random() % 5 // 0...4
그러나 이것은 약간의 모듈로 바이어스를 생성하므로 ( 여기 및 여기 참조 ) 이것이 arc4random_uniform()
권장되는 이유 입니다.
와 (과) 변환 Int
일반적으로는 변환 후면과 등 사이에 위해 이런 일을 할 괜찮을 것 Int
과 UInt32
:
let number: Int = 10
let random = Int(arc4random_uniform(UInt32(number)))
문제는, 그러나, 즉 Int
범위 갖는 -2,147,483,648...2,147,483,647
32 비트 시스템과의 범위를 -9,223,372,036,854,775,808...9,223,372,036,854,775,807
64 개 비트 시스템에있다. 이것을의 UInt32
범위와 비교하십시오 0...4,294,967,295
. 의 U
는 부호없는UInt32
것을 의미 합니다.
다음 오류를 고려하십시오.
UInt32(-1) // negative numbers cause integer overflow error
UInt32(4294967296) // numbers greater than 4,294,967,295 cause integer overflow error
따라서 입력 매개 변수가 UInt32
범위 내에 있고 해당 범위를 벗어난 출력이 필요하지 않아야합니다.
10 (0-9)의 난수에 대한 예;
import UIKit
let randomNumber = Int(arc4random_uniform(10))
매우 쉬운 코드-간단하고 짧습니다.
난 그냥 rand()
임의의 CInt를 얻기 위해 사용할 수있었습니다 . 다음과 같이 사용하여 Int로 만들 수 있습니다.
let myVar: Int = Int(rand())
자주 사용하는 C 랜덤 함수를 사용할 수 있으며 필요한 경우 값을 Int로 변환하면됩니다.
@jstn의 대답 은 좋지만 조금 장황합니다. Swift는 프로토콜 지향 언어로 알려져 있으므로 프로토콜 확장에 기본 구현을 추가하여 정수 패밀리의 모든 클래스에 대해 상용구 코드를 구현하지 않고도 동일한 결과를 얻을 수 있습니다.
public extension ExpressibleByIntegerLiteral {
public static func arc4random() -> Self {
var r: Self = 0
arc4random_buf(&r, MemoryLayout<Self>.size)
return r
}
}
이제 우리는 할 수 있습니다 :
let i = Int.arc4random()
let j = UInt32.arc4random()
그리고 다른 모든 정수 클래스는 괜찮습니다.
Swift 4.2 에서는 random()
원하는 숫자 유형에 대해 메소드를 호출하여 임의의 숫자를 생성 하여 작업하려는 범위를 제공 할 수 있습니다. 예를 들어, 이것은 양쪽에서 1에서 9 사이의 난수를 생성합니다.
let randInt = Int.random(in: 1..<10)
다른 유형과도
let randFloat = Float.random(in: 1..<20)
let randDouble = Double.random(in: 1...30)
let randCGFloat = CGFloat.random(in: 1...40)
다음은 잘 작동하는 라이브러리입니다 https://github.com/thellimist/SwiftRandom
public extension Int {
/// SwiftRandom extension
public static func random(lower: Int = 0, _ upper: Int = 100) -> Int {
return lower + Int(arc4random_uniform(UInt32(upper - lower + 1)))
}
}
public extension Double {
/// SwiftRandom extension
public static func random(lower: Double = 0, _ upper: Double = 100) -> Double {
return (Double(arc4random()) / 0xFFFFFFFF) * (upper - lower) + lower
}
}
public extension Float {
/// SwiftRandom extension
public static func random(lower: Float = 0, _ upper: Float = 100) -> Float {
return (Float(arc4random()) / 0xFFFFFFFF) * (upper - lower) + lower
}
}
public extension CGFloat {
/// SwiftRandom extension
public static func random(lower: CGFloat = 0, _ upper: CGFloat = 1) -> CGFloat {
return CGFloat(Float(arc4random()) / Float(UINT32_MAX)) * (upper - lower) + lower
}
}
let MAX : UInt32 = 9
let MIN : UInt32 = 1
func randomNumber()
{
var random_number = Int(arc4random_uniform(MAX) + MIN)
print ("random = ", random_number);
}
Swift 책의 난수 생성기 예제가 선형 합동 생성기 (LCG)라는 기존 답변에 추가하고 싶습니다. 매우 제한적이며 무작위의 품질이 중요하지 않은 간단한 예제를 제외하면 안됩니다. 전혀 중요하지 않습니다. 그리고 LCG 암호화 목적으로 사용해서는 안됩니다 .
arc4random()
훨씬 낫고 대부분의 목적으로 사용될 수 있지만 암호화 목적으로 다시 사용 해서는 안됩니다.
암호로 안전한 것을 원하면을 사용하십시오 SecCopyRandomBytes()
. 난수 생성기를 무언가로 만들면 다른 사람이 암호 목적 (암호, 키 또는 소금 생성과 같은)을 위해 그것을 사용 SecCopyRandomBytes()
하게 될 수 있습니다 (필요한 경우에도). 그럴 필요는 없습니다.
스위프트 4.2 부터
새로운 API 세트가 있습니다.
let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomDouble = Double.random(in: 1 ... 10)
모든 숫자 유형에는 이제
random(in:)
메소드가range
있습니다.해당 범위에 균일하게 분포 된 숫자를 반환합니다.
TL; DR
글쎄, "좋은"옛날 방식에 어떤 문제가 있는가?
가져온 C API 를 사용해야합니다 (플랫폼마다 다릅니다) .
그리고 ...
랜덤이 랜덤이 아니라고 말하면 어떻게 되나요?
와 같이 ( arc4random()
나머지를 계산하기 위해) 를 사용 arc4random() % aNumber
하면 결과 가0
와 사이에 균일하게 분포 되지 않습니다aNumber
. 모듈로 바이어스 라는 문제가 있습니다 .
모듈로 바이어스
일반적으로 함수 사이의 난수를 생성 0
하고 MAX는 (종류 등에 의존한다) . 빠르고 쉬운 예를 들어, 최대 숫자가 7
있고 범위 내 임의의 숫자 0 ..< 2
(또는 원하는 경우 간격 [0, 3)에 관심 이 있다고 가정 해 봅시다 .
개별 숫자 의 확률 은 다음과 같습니다.
- 0 : 3/8 = 37.5 %
- 1 : 3/8 = 37.5 %
- 2 : 2/8 = 25 %
즉, 당신은 가능성 으로 끝낼 수 0 또는 1 에 비해 2 . 물론 이것은 매우 단순화되고 MAX 숫자가 훨씬 높아서 더 "공정" 하다는 것을 명심하십시오 .
var randomNumber = Int(arc4random_uniform(UInt32(**5**)))
여기서 5는 0에서 5까지의 난수가 생성되도록합니다. 그에 따라 값을 설정할 수 있습니다.
Xcode의 일부 버전에서 arc4Random_uniform ()이 없으면 (7.1에서는 실행되지만 자동 완성되지는 않습니다). 대신이 작업을 수행 할 수 있습니다.
0-5에서 난수를 생성합니다. 먼저
import GameplayKit
그때
let diceRoll = GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
스위프트 4.2
파운데이션 C 라이브러리 가져 오기 arc4random_uniform()
// 1
let digit = Int.random(in: 0..<10)
// 2
if let anotherDigit = (0..<10).randomElement() {
print(anotherDigit)
} else {
print("Empty range.")
}
// 3
let double = Double.random(in: 0..<1)
let float = Float.random(in: 0..<1)
let cgFloat = CGFloat.random(in: 0..<1)
let bool = Bool.random()
- random (in :)을 사용하여 범위에서 임의의 숫자를 생성합니다.
- randomElement ()는 범위가 비어 있으면 nil을 반환하므로 반환 된 Int를 줄 바꿈 하시겠습니까? 하자면.
- random (in :)을 사용하여 임의의 Double, Float 또는 CGFloat 및 random ()을 생성하여 임의의 Bool을 반환합니다.
다음 코드는 0에서 255 사이의 안전한 임의의 숫자를 생성합니다.
extension UInt8 {
public static var random: UInt8 {
var number: UInt8 = 0
_ = SecRandomCopyBytes(kSecRandomDefault, 1, &number)
return number
}
}
당신은 이것을 다음과 같이 부릅니다.
print(UInt8.random)
숫자가 클수록 복잡해집니다.
이것은 내가 생각해 낼 수있는 최선입니다.
extension UInt16 {
public static var random: UInt16 {
let count = Int(UInt8.random % 2) + 1
var numbers = [UInt8](repeating: 0, count: 2)
_ = SecRandomCopyBytes(kSecRandomDefault, count, &numbers)
return numbers.reversed().reduce(0) { $0 << 8 + UInt16($1) }
}
}
extension UInt32 {
public static var random: UInt32 {
let count = Int(UInt8.random % 4) + 1
var numbers = [UInt8](repeating: 0, count: 4)
_ = SecRandomCopyBytes(kSecRandomDefault, count, &numbers)
return numbers.reversed().reduce(0) { $0 << 8 + UInt32($1) }
}
}
이 방법들은 여분의 난수 UInt8
를 사용하여 난수를 만드는 데 사용될 수를 결정합니다 . 마지막 줄은 변환 [UInt8]
에 UInt16
나 UInt32
.
마지막 두 가지가 여전히 무작위로 계산되는지는 모르겠지만 원하는대로 조정할 수 있습니다. :)
스위프트 4.2
Swift 4.2는 표준 라이브러리에 기본 기능을 갖춘 모든 기능을 갖춘 임의의 숫자 API를 포함했습니다. ( 스위프트 에볼루션 제안 SE-0202 )
let intBetween0to9 = Int.random(in: 0...9)
let doubleBetween0to1 = Double.random(in: 0...1)
모든 숫자 유형에는 정적 임의의 (in :) 이 있으며 범위를 가져 와서 주어진 범위의 임의의 숫자를 반환합니다
스위프트 4.2, Xcode 10.1 .
iOS, macOS 및 tvOS 의 경우 Xcode의 프레임 워크에서 시스템 전체의 임의 소스 를 사용할 수 있습니다 GameKit
. 클래스 메소드를 GKRandomSource
사용하여 sharedRandom()
클래스를 찾을 수 있습니다 .
import GameKit
let number: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
func randomGenerator() -> Int {
let random = GKRandomSource.sharedRandom().nextInt(upperBound: number.count)
return number[random]
}
randomGenerator()
또는 randomElement()
컬렉션의 임의 요소를 반환 하는 메서드를 사용하십시오 .
let number: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let randomNumber = number.randomElement()!
print(randomNumber)
다음 GeneratorOf
과 같이 사용할 수 있습니다 :
var fibs = ArraySlice([1, 1])
var fibGenerator = GeneratorOf{
_ -> Int? in
fibs.append(fibs.reduce(0, combine:+))
return fibs.removeAtIndex(0)
}
println(fibGenerator.next())
println(fibGenerator.next())
println(fibGenerator.next())
println(fibGenerator.next())
println(fibGenerator.next())
println(fibGenerator.next())
이 코드를 사용하여 난수를 생성합니다.
//
// FactModel.swift
// Collection
//
// Created by Ahmadreza Shamimi on 6/11/16.
// Copyright © 2016 Ahmadreza Shamimi. All rights reserved.
//
import GameKit
struct FactModel {
let fun = ["I love swift","My name is Ahmadreza","I love coding" ,"I love PHP","My name is ALireza","I love Coding too"]
func getRandomNumber() -> String {
let randomNumber = GKRandomSource.sharedRandom().nextIntWithUpperBound(fun.count)
return fun[randomNumber]
}
}
세부
x 코드 9.1, 스위프트 4
수학 중심 솔루션 (1)
import Foundation
class Random {
subscript<T>(_ min: T, _ max: T) -> T where T : BinaryInteger {
get {
return rand(min-1, max+1)
}
}
}
let rand = Random()
func rand<T>(_ min: T, _ max: T) -> T where T : BinaryInteger {
let _min = min + 1
let difference = max - _min
return T(arc4random_uniform(UInt32(difference))) + _min
}
솔루션 사용 (1)
let x = rand(-5, 5) // x = [-4, -3, -2, -1, 0, 1, 2, 3, 4]
let x = rand[0, 10] // x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
프로그래머 중심 솔루션 (2)
수학 중심 솔루션 (1) 코드를 여기 에 추가하는 것을 잊지 마십시오
import Foundation
extension CountableRange where Bound : BinaryInteger {
var random: Bound {
return rand(lowerBound-1, upperBound)
}
}
extension CountableClosedRange where Bound : BinaryInteger {
var random: Bound {
return rand[lowerBound, upperBound]
}
}
솔루션 사용 (2)
let x = (-8..<2).random // x = [-8, -7, -6, -5, -4, -3, -2, -1, 0, 1]
let x = (0..<10).random // x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
let x = (-10 ... -2).random // x = [-10, -9, -8, -7, -6, -5, -4, -3, -2]
전체 샘플
여기 에 솔루션 (1) 및 솔루션 (2) 코드 를 추가하는 것을 잊지 마십시오
private func generateRandNums(closure:()->(Int)) {
var allNums = Set<Int>()
for _ in 0..<100 {
allNums.insert(closure())
}
print(allNums.sorted{ $0 < $1 })
}
generateRandNums {
(-8..<2).random
}
generateRandNums {
(0..<10).random
}
generateRandNums {
(-10 ... -2).random
}
generateRandNums {
rand(-5, 5)
}
generateRandNums {
rand[0, 10]
}
샘플 결과
'Programing' 카테고리의 다른 글
버튼 텍스트가 Lollipop의 모든 캡으로 강제되는 이유는 무엇입니까? (0) | 2020.02.21 |
---|---|
Java를 사용하여 인터넷에서 파일을 다운로드하고 저장하는 방법은 무엇입니까? (0) | 2020.02.20 |
SSH 키-여전히 비밀번호 및 비밀번호 문구 요청 (0) | 2020.02.20 |
Git 저장소에서 특정 작성자가 변경 한 총 줄 수를 계산하는 방법은 무엇입니까? (0) | 2020.02.20 |
Google Finance API 대신 사용 (0) | 2020.02.20 |