Swift4.2 Random API 정보
아직 변경이 많기 때문에 최신 정보는 swift 창고를 참조하세요.
Swift4.2에서 가져온 Random API 정보
Random API가 필요한 이유
iOS/macOS 개발의 경우 지금까지 사용되고 있음arc4random_uniform
.
그러나 크로스플랫폼 개발에서 랜덤 선택 알고리즘 등을 사용하려면 리눅스 환경에 존재하지 않는다arc4random_uniform
는 자체 준비가 필요하다.
리눅스 환경으로 설치할 때 하도급은 Upper Bound를 사용하지 않는 함수random
와 rand
등을 사용할 수 있다.
협조arc4random_uniform
를 위해 매개 변수upperBound
로 여수를 찾으려고 생각할 수 있지만 그 방법이라면 모듈로 비아스가 생길 수 있다.대부분의 경우 모루비아스는 작아 눈에 띄거나 영향을 주지 않지만, 최악의 경우 원소 간 선택 확률이 2배로 높아진다.그래서 일반적인 실현에서 사용해서는 안 된다.
크로스플랫폼 확인도 번거로우니 표준으로 준비해 주시기 바랍니다!
이런 내용은 원숭이를 홍보하는 모티베이션에 적혀 있다.
https://github.com/apple/swift-evolution/blob/master/proposals/0202-random-unification.md
RandomNumberGenerator
프로토콜은 무작위 생성기의 인터페이스RandomNumberGenerator
입니다.public protocol RandomNumberGenerator {
// This determines the functionality for producing a random number.
// Required to implement by all RNGs.
mutating func next() -> UInt64
}
이외에도 next<T : FixedWidthInteger & UnsignedInteger>() -> T
와 next<T : FixedWidthInteger & UnsignedInteger>(upperBound: T) -> T
의 기본 설치가 준비되어 있으며, 설치 생성UInt64
의 부분만 사용하면 된다.특히 후자upperBound
가 덧붙인 함수는Modulo Bias 설치 고려로 안전하게 사용할 수 있다.
Random
그리고 공식적으로 준비한 RNG의 실현은Random
(그런데 이름은변경 예정인 것 같다.Random
각 플랫폼은 적당한 청부업자 함수를 사용하는데 이걸로 쓰면 교차 플랫폼에서의 실현이 간단해진다.Random
에 따르면 초기화할 필요가 없고 라인이 안전하다는 점은 공통적이지만 cryptographic quality는 청부에 따라 바뀐다고 쓰여 있기 때문에 주의해야 한다(후술).Random
는 struct이지만 내부 상태가 없기 때문에 Random.default
는 매번 새로운 실례를 되돌려준다.default
의 준비는 다음과 같은 쓰기 유형의 실현을 위한 것이다.
종류별 랜덤 생성
static 방법FIxedWidthInteger.random(in: Range<Self>, using: inout RandomNumberGenerator)
과 BinaryFloatingPoint.random(in: Range<Self>, using: inout RandomNumberGenerator)
등이 추가됐다.
수치 이외Collection
에도 추가Collection.randomElement(using: inout RandomNumberGenerator)
됐다.
생략using
한 경우Random.default
에는 기본적으로 배후의RandomNumberGenerator
API를 무의식적으로 사용했다.
이 디자인이라면 Random()
한 줄로 using 처리를 하지 않기 때문에 준비했습니다Random.default
.
총결산
랜덤수 주변arc4random_uniform
은 주로 다양한 스타일을 직접 준비하며 랜덤API 구현에 따라 시간을 크게 단축할 수 있다.
Swift4.2부터 랜덤 API를 사용하여 크로스 플랫폼을 쉽게 재활용할 수 있는 프로그램을 작성해 봅시다.
보: Random은 Cryptographically secure입니까?
참고 자료
///While the Random.default
generator is automatically seeded and
///thread-safe on every platform, the cryptographic quality of the stream of
///random data produced by the generator may vary. For more detail, see the
///documentation for the APIs used by each platform.
///
///- Apple platforms use arc4random_buf(3)
.
///- Linux platforms use getrandom(2)
when available; otherwise, they read
/// from /dev/urandom
.
또한 랜덤의 댓글.
The aspiration is that this RNG should be cryptographically secure, provide reasonable performance, and should be thread safe. If a vendor is unable to provide these goals, they should document it clearly.
후자의 항목 이름은 Random Number Generator로 매우 번거롭지만 이곳의 대상은 RandomNumberGenerator
프로토콜이 아니라 Random
(default RNG)와 그 청부의 실현이다.
현재Random
의 세 청부업자는 모두 cryptographically secure인 것 같지만, cryptographically secure가 설치된 플랫폼을 준비할 수 없다는 점을 감안하면Random
자체의 security는 보장할 수 없을 것 같다.
안전성이 요구되는 곳에서cryptographicallysecure를 보장할 수 있는 수단을 사용하는 것이 좋을 것 같다(말하자면 unsecure의 설치를 더하고 그 플랫폼을 사용하는 경우는 많지 않은 것 같다).
그나저나 thread safety에 대한 논평은 단언된 것이고, 프로 레슬링에서는 애매한 것이며, 어느 것이 옳은 것일까?
추가: 부동 소수점 수치에 대한 무작위 생성
우리도 부동점 수치의 생성을 조사하고 이어서 총괄한다.
부동 소수점 값 정보
부동 소수점 수치는 $(기호)*2^{(지수 부분)}*1입니다.(잠정 부분) 달러로 표현1. (잠정 부분) 달러는 2진법1.0..<10.0 (=2)
의 범위를 채택한다.
부동점수치1..<2
의 범위 내에는 $2^{(임시부분 비트수)}달러의 점만 있고, 이 구간의 지수 부분은 $0$1이기 때문에 임시부분 증가량의 증가량은 점 사이의 간격이고, 더블 상황은 $2^{-52}달러이다.이는 Swift에서 BinaryFloatingPoint.ulpOfOne
로 정의됩니다.
그렇다면1.0.nextUp - 1 == .ulpOfOne
의 범위도 같은 일을 고려해 보자.지수부는1/2..<1
범위의 지수부-1이고, 다른 한편, 소수부의 자릿수는 변하지 않는다=분수는 변하지 않기 때문에 점의 간격은 $2^{53}$이며, Swift1..<2
에 있다.
마찬가지로 .ulpOfOne/2
1/4..<1/2
범위 내에서 간격이 점점 반으로 바뀌었다.반면1/8..<1/4
2..<4
의 범위 내에서 부동점 수치의 절대치는 작을수록 촘촘하고 크면 클수록 희소하다.
[0,1) 구간 내의 고른 표본 추출
부동 소수점 수치의 생성에 관해서는 포럼에서 인용한 다음과 같은 내용을 참고하시오.
SE-0202 Random Unification#Random Number GeneratorGenerating uniform doubles in the unit interval 항목
즉 생성4..<8
하려면 통일 랜덤수Double
를 사용하면 된다.r: UInt64
는 Double(r >> 11) * (.ulpOfOne / 2)
구간의 점의 간격이고 아래로 내려가는 구간의 점의 간격은 절반에 불과하기 때문에 (.ulpOfOne / 2)
전체 범위 내에서 이 각도를 사용할 수 있다.1/2..<1
는 53비트 랜덤수, 즉 0..<1
를 사용하고 상기 Double(r >> 11)
는 $2^{-53}로 조합을 통해 0..<2**53
의 랜덤수를 생성할 수 있다.
이보다 더 작은 각도를 사용하면(.ulpOfOne / 2)
0..<1
반올림이 생겨 최대치.ulpOfOne/4
를 생성할 수 있습니다.// Floatの場合
print(Float(UInt32.max >> 8) * (.ulpOfOne / 2) < 1) // 2^-23刻み true
print(Float(UInt32.max >> 7) * (.ulpOfOne / 4) < 1) // 2^-24刻み false
// Doubleの場合
print(Double(UInt64.max >> 11) * (.ulpOfOne / 2) < 1) // 2^-53刻み true
print(Double(UInt64.max >> 10) * (.ulpOfOne / 4) < 1) // 2^-54刻み false
반대로 큰 눈금, 예를 들어1/2..<1
을 사용하면 고르게 샘플을 채취할 수 있지만 간격이 커지면 생성할 수 있는 값의 변화가 반으로 바뀐다.
따라서 1.0
구간에서 샘플을 채취하면 .ulpOfOne
눈금이 가장 좋다.0..<1
의.ulpOfOne/2
는 위에서 설명한 방법으로 [0,1)을 생성한 것으로 앞으로 주의하지 않아도 자연스럽게 좋은 실현을 사용할 수 있다.
Swift 구간에서 균일한 샘플링 수행
$구간BinaryFloatingPoint.random(in: range)
을 얻을 수 있기 때문에 [low, high)
라면 $low,high를 원합니다.
또 r
에서 취한 경우(high - low) * r + low
의 최대치1..<2
,0..<1
의 최대치1.nextDown
를 고려했다.1..<2
구간이어서 2.nextDown
는 1배로 늘었지만 부동점수의 규격은1..<2
0..<1
과1.nextDonw * 2 == 2.nextDonw
사이였다.
실제 계산하면 결과가 둥글게 1.nextDonw + 1
되어 범위에서 벗어난다.let r = Float(UInt32.max >> 8) * (.ulpOfOne / 2)
print(r == 1) // false
print(r+1 == 2) // true
우리는 이러한 계산 오차로 범위를 벗어난 상황을 처리하는 방법을 조사했지만 계산 결과2.nextDown
이상이 되면 취소하는 방법을 발견했다.
스위프트도 이 오류가 있었지만 같은 방법으로 수정http://xoshiro.di.unimi.it/
표준 정적 분포 샘플링
만약에 균등한 분포에서 샘플링을 할 수 있다면 다음과 같은 방법을 통해 표준 정적 분포에서 샘플링을 할 수 있다.
public protocol RandomNumberGenerator {
// This determines the functionality for producing a random number.
// Required to implement by all RNGs.
mutating func next() -> UInt64
}
참고 자료
///While the
Random.default
generator is automatically seeded and///thread-safe on every platform, the cryptographic quality of the stream of
///random data produced by the generator may vary. For more detail, see the
///documentation for the APIs used by each platform.
///
///- Apple platforms use
arc4random_buf(3)
.///- Linux platforms use
getrandom(2)
when available; otherwise, they read/// from
/dev/urandom
.또한 랜덤의 댓글.
The aspiration is that this RNG should be cryptographically secure, provide reasonable performance, and should be thread safe. If a vendor is unable to provide these goals, they should document it clearly.
후자의 항목 이름은 Random Number Generator로 매우 번거롭지만 이곳의 대상은
RandomNumberGenerator
프로토콜이 아니라 Random
(default RNG)와 그 청부의 실현이다.현재
Random
의 세 청부업자는 모두 cryptographically secure인 것 같지만, cryptographically secure가 설치된 플랫폼을 준비할 수 없다는 점을 감안하면Random
자체의 security는 보장할 수 없을 것 같다.안전성이 요구되는 곳에서cryptographicallysecure를 보장할 수 있는 수단을 사용하는 것이 좋을 것 같다(말하자면 unsecure의 설치를 더하고 그 플랫폼을 사용하는 경우는 많지 않은 것 같다).
그나저나 thread safety에 대한 논평은 단언된 것이고, 프로 레슬링에서는 애매한 것이며, 어느 것이 옳은 것일까?
추가: 부동 소수점 수치에 대한 무작위 생성
우리도 부동점 수치의 생성을 조사하고 이어서 총괄한다.
부동 소수점 값 정보
부동 소수점 수치는 $(기호)*2^{(지수 부분)}*1입니다.(잠정 부분) 달러로 표현1. (잠정 부분) 달러는 2진법1.0..<10.0 (=2)
의 범위를 채택한다.
부동점수치1..<2
의 범위 내에는 $2^{(임시부분 비트수)}달러의 점만 있고, 이 구간의 지수 부분은 $0$1이기 때문에 임시부분 증가량의 증가량은 점 사이의 간격이고, 더블 상황은 $2^{-52}달러이다.이는 Swift에서 BinaryFloatingPoint.ulpOfOne
로 정의됩니다.
그렇다면1.0.nextUp - 1 == .ulpOfOne
의 범위도 같은 일을 고려해 보자.지수부는1/2..<1
범위의 지수부-1이고, 다른 한편, 소수부의 자릿수는 변하지 않는다=분수는 변하지 않기 때문에 점의 간격은 $2^{53}$이며, Swift1..<2
에 있다.
마찬가지로 .ulpOfOne/2
1/4..<1/2
범위 내에서 간격이 점점 반으로 바뀌었다.반면1/8..<1/4
2..<4
의 범위 내에서 부동점 수치의 절대치는 작을수록 촘촘하고 크면 클수록 희소하다.
[0,1) 구간 내의 고른 표본 추출
부동 소수점 수치의 생성에 관해서는 포럼에서 인용한 다음과 같은 내용을 참고하시오.
SE-0202 Random Unification#Random Number GeneratorGenerating uniform doubles in the unit interval 항목
즉 생성4..<8
하려면 통일 랜덤수Double
를 사용하면 된다.r: UInt64
는 Double(r >> 11) * (.ulpOfOne / 2)
구간의 점의 간격이고 아래로 내려가는 구간의 점의 간격은 절반에 불과하기 때문에 (.ulpOfOne / 2)
전체 범위 내에서 이 각도를 사용할 수 있다.1/2..<1
는 53비트 랜덤수, 즉 0..<1
를 사용하고 상기 Double(r >> 11)
는 $2^{-53}로 조합을 통해 0..<2**53
의 랜덤수를 생성할 수 있다.
이보다 더 작은 각도를 사용하면(.ulpOfOne / 2)
0..<1
반올림이 생겨 최대치.ulpOfOne/4
를 생성할 수 있습니다.// Floatの場合
print(Float(UInt32.max >> 8) * (.ulpOfOne / 2) < 1) // 2^-23刻み true
print(Float(UInt32.max >> 7) * (.ulpOfOne / 4) < 1) // 2^-24刻み false
// Doubleの場合
print(Double(UInt64.max >> 11) * (.ulpOfOne / 2) < 1) // 2^-53刻み true
print(Double(UInt64.max >> 10) * (.ulpOfOne / 4) < 1) // 2^-54刻み false
반대로 큰 눈금, 예를 들어1/2..<1
을 사용하면 고르게 샘플을 채취할 수 있지만 간격이 커지면 생성할 수 있는 값의 변화가 반으로 바뀐다.
따라서 1.0
구간에서 샘플을 채취하면 .ulpOfOne
눈금이 가장 좋다.0..<1
의.ulpOfOne/2
는 위에서 설명한 방법으로 [0,1)을 생성한 것으로 앞으로 주의하지 않아도 자연스럽게 좋은 실현을 사용할 수 있다.
Swift 구간에서 균일한 샘플링 수행
$구간BinaryFloatingPoint.random(in: range)
을 얻을 수 있기 때문에 [low, high)
라면 $low,high를 원합니다.
또 r
에서 취한 경우(high - low) * r + low
의 최대치1..<2
,0..<1
의 최대치1.nextDown
를 고려했다.1..<2
구간이어서 2.nextDown
는 1배로 늘었지만 부동점수의 규격은1..<2
0..<1
과1.nextDonw * 2 == 2.nextDonw
사이였다.
실제 계산하면 결과가 둥글게 1.nextDonw + 1
되어 범위에서 벗어난다.let r = Float(UInt32.max >> 8) * (.ulpOfOne / 2)
print(r == 1) // false
print(r+1 == 2) // true
우리는 이러한 계산 오차로 범위를 벗어난 상황을 처리하는 방법을 조사했지만 계산 결과2.nextDown
이상이 되면 취소하는 방법을 발견했다.
스위프트도 이 오류가 있었지만 같은 방법으로 수정http://xoshiro.di.unimi.it/
표준 정적 분포 샘플링
만약에 균등한 분포에서 샘플링을 할 수 있다면 다음과 같은 방법을 통해 표준 정적 분포에서 샘플링을 할 수 있다.
// Floatの場合
print(Float(UInt32.max >> 8) * (.ulpOfOne / 2) < 1) // 2^-23刻み true
print(Float(UInt32.max >> 7) * (.ulpOfOne / 4) < 1) // 2^-24刻み false
// Doubleの場合
print(Double(UInt64.max >> 11) * (.ulpOfOne / 2) < 1) // 2^-53刻み true
print(Double(UInt64.max >> 10) * (.ulpOfOne / 4) < 1) // 2^-54刻み false
let r = Float(UInt32.max >> 8) * (.ulpOfOne / 2)
print(r == 1) // false
print(r+1 == 2) // true
Ziggurat algorithm
지그재그는 고속이라고 하지만 특별히 최적화되지 않은 상태에서 가장 느리다.나머지 2개는 https://github.com/t-ae/ZigguratSampler/blob/5c1d6d391c34c471f8f23b9e729f689f84ff0cad/Tests/ZigguratSamplerTests/ZigguratSamplerTests.swift#L35-L86가 쓴 것처럼 Box-Muller가 더 빠르다.
위 단계
2
의 구현을 균일 분포 생성에 사용하면 Box-Muller보다 polar method로 더 넓은 범위의 값을 생성할 수 있습니다.import Foundation
func boxmuller(u1: Double, u2: Double) -> Double {
return sqrt(-2 * log(u1)) * sin(2 * .pi * u2)
}
func polar(u1: Double, u2: Double) -> Double {
let r = u1*u1 + u2*u2
assert(0 < r && r < 1)
return sqrt(-2 * log(r) / r) * u1
}
// 最大値
print(boxmuller(u1: .ulpOfOne/2, u2: 0.25)) // 8.571674348652905
print(polar(u1: (0.5.nextUp)*2-1, u2: 0)) // 12.00727336061225
// 0に近い値
print(boxmuller(u1: 1.0.nextDown, u2: Double.ulpOfOne/2)) // 1.0394658142353987e-23
print(polar(u1: (0.5.nextUp)*2-1, u2: 1.0.nextDown*2-1)) // 6.617444900424223e-24
그렇다고 정규 분포로 더 큰 값을 만들 수도 없고, 어떤 방법이 가장 적절한지 모르니 여러분도 공식적으로 준비해주시기 바랍니다.
Reference
이 문제에 관하여(Swift4.2 Random API 정보), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/t-ae/items/1c5c2415c4469e6e6713텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)