동전이 하나 있습니다. 원하는만큼 뒤집을 수 있습니다.
여기서 되도록 난수 을 생성하려고합니다 .a ≤ r < b r , a , b ∈ Z +
숫자의 분포는 균일해야합니다.
이면 쉽습니다 .
r = a + binary2dec(flip n times write 0 for heads and 1 for tails)
만약 ?
동전이 하나 있습니다. 원하는만큼 뒤집을 수 있습니다.
여기서 되도록 난수 을 생성하려고합니다 .a ≤ r < b r , a , b ∈ Z +
숫자의 분포는 균일해야합니다.
이면 쉽습니다 .
r = a + binary2dec(flip n times write 0 for heads and 1 for tails)
만약 ?
답변:
당신이 찾고있는 것은 거부 샘플링 또는 수락 거부 방법을 기반으로 합니다 (위키 페이지는 약간 기술적입니다).
이 방법은 다음과 같은 상황에서 유용합니다. 세트에서 임의의 객체를 선택하고 싶습니다 ( 귀하의 경우 세트의 임의의 정수 ).하지만 그 방법을 모르지만 첫 번째 세트를 포함하는 더 큰 세트에서 임의의 객체를 선택할 수 있습니다 (귀하의 경우, 일부 k 의 경우 [ a , 2 k + a ] , 2 k + a ≥ b , 이것은 k 코인 플립에 해당 ).
이러한 시나리오에서는 더 작은 세트에서 요소를 임의로 선택할 때까지 더 큰 세트에서 요소를 계속 선택합니다. 더 작은 세트가 더 큰 세트에 비해 충분히 큰 경우 (이 경우 는 [ a , b ] 보다 최대 2 배 많은 정수를 포함 합니다.), 이것은 효율적입니다.
대안적인 예 : 반지름이 1 인 원 내부에서 임의의 점을 선택한다고 가정 해 봅시다. 이제이를위한 직접적인 방법을 생각해내는 것은 쉽지 않습니다. accept-reject 메소드로 돌아갑니다 : 원을 둘러싸고있는 1x1 정사각형으로 점을 샘플링하고 우리가 그리는 숫자가 원 안에 있는지 테스트합니다.
(기술 : 답은 숫자 선택에 적합 )
원하는만큼 동전을 뒤집을 수 있기 때문에 분수 을 선택하여 원하는만큼 근접한 확률을 얻을 수 있습니다 (바이너리 기수 사용 : 점 뒤에 각 숫자에 대한 동전)과 r 에 b - a 를 곱하여 0과 [ba-1] 사이의 숫자 (정수로 내림)를 얻습니다. 이 번호를 추가 를 하면됩니다.
예 : say . 이진수의 1/3은 0.0101010101 ...입니다. 그러면 플립이 0에서 0.010101 사이이면 선택이 b 입니다. 이 beween 0.010101 .. 및 0.10101010 경우 ... 당신의 선택이 될 것입니다 + 1 , 그렇지 않으면이 될 것입니다 + 2 .
동전을 번 뒤집 으면 a 와 b 사이 의 각 숫자 가 확률로 선택됩니다 1.
다음 2의 거듭 제곱에서 숫자를 선택하고 보다 큰 답을 버리십시오 .
n = b-a;
N = round_to_next_larger_power_of_2(n)
while (1) {
x = random(0 included to N excluded);
if (x < n) break;
}
r = a + x;
아무도 이것을 언급하지 않았으므로, 가 2의 거듭 제곱이 아닌 도메인 인 경우 , 엄청나게 많은 공정한 동전 던지기가 D 의 균일 한 무작위 구성원을 생성하기에 충분하지 않다는 것을 공식적으로 증명하겠습니다 . k 코인을 사용하여 D 의 멤버를 생성 한다고 가정하십시오 . 각 D ∈ D , 알고리즘은 발생 확률 D는 양식이다 / 2 K 일부 정수 대 . 산술의 기본 정리는 A / 2 k ≠ 1 / | D | .
D의 독립적 인 균일 샘플 을 생성 하려면 필요한 최적 코인 토스 수 (최적의 알고리즘 하에서)는 대략 n log 2 | D | . 당신이 엔트로피의 분포에서 생성 할 경우 더 일반적으로, H , 당신은 대략 필요 N H 기대 무작위 비트를. 이를 달성하는 알고리즘은 (임의로) 무한의 랜덤 비트 시퀀스에 적용되는 산술 디코딩입니다.
ba가 2의 거듭 제곱이 아니면 결과를 얻기 위해 많은 동전을 뒤집어 야 할 수도 있습니다. 결과를 얻지 못할 수도 있지만 극단적 인 결과는 아닙니다.
행동 양식
가장 간단한 방법은 [a, a + 2 ^ n)에서 숫자를 생성하는 것입니다. 여기서 2 ^ n> = ba는 [a, b)에 도달 할 때까지입니다. 이 방법으로 많은 엔트로피를 버립니다.
더 비싼 방법을 사용하면 모든 엔트로피를 유지할 수 있지만 코인 플립 / 주사위 롤 수가 증가함에 따라 계산 상 매우 비쌉니다. 직관적으로 그것은 동전을 소수점 오른쪽의 이진수의 숫자로 뒤집는 것을 처리하여 그 숫자를 밑이 2에서 밑으로 ab로 변환하고 그 숫자가 '고착'될 때 그 숫자를 반환하는 것과 같습니다.
예
다음 코드는 롤 수가 증가함에 따라 한계 비용이 증가하면서 공정한 n면 다이의 롤을 공정한 m면 다이의 롤 (여기서 n = 2, m = ab)로 변환합니다. 임의의 정밀도를 가진 유리수 유형이 필요하다는 점에 유의하십시오. 한 가지 좋은 속성은 n면에서 m면으로, 다시 n면으로 변환하면 원래 스트림을 반환하지만 숫자가 붙어서 몇 롤에 의해 지연 될 수 있다는 것입니다.
public static IEnumerable<BigInteger> DigitConversion(this IEnumerable<BigInteger> inputStream, BigInteger modIn, BigInteger modOut) {
//note: values are implicitly scaled so the first unfixed digit of the output ranges from 0 to 1
Rational b = 0; //offset of the chosen range
Rational d = 1; //size of the chosen range
foreach (var r in inputStream) {
//narrow the chosen range towards the real value represented by the input
d /= modIn;
b += d * r;
//check for output digits that have become fixed
while (true) {
var i1 = (b * modOut).Floor();
var i2 = ((b + d) * modOut).Floor(); //note: ideally b+d-epsilon, but another iteration makes that correction unnecessary
if (i1 != i2) break; //digit became fixed?
//fix the next output digit (rescale the range to make next digit range from 0 to 1)
d *= modOut;
b *= modOut;
b -= i1;
yield return i1;
}
}
}
이진수를 생성합니다. 명시 적으로 저장하는 대신 최소 및 최대 가능한 값을 추적하십시오. 해당 값이 동일한 정수 내에 있으면 해당 정수를 반환하십시오. 코드 스케치는 다음과 같습니다.
(편집) 자세한 설명 : 1/3 확률로 1에서 3까지의 임의의 정수를 생성한다고 가정하십시오. 우리는 (0, 1) 범위에서 임의의 이진수 십진 실수 x를 생성하여이를 수행합니다. x <1/3이면 1을, 그렇지 않으면 x <2/3는 2를, 그렇지 않으면 3을 반환합니다. x의 숫자를 명시 적으로 생성하는 대신 x의 가능한 최소값과 최대 값을 추적합니다. 처음에 x의 최소값은 0이고 최대 값은 1입니다. 머리를 먼저 뒤집 으면 소수점 뒤의 x의 첫 번째 숫자 (2 진)는 1입니다. x (2 진)의 가능한 최소값은 0.100000이됩니다. = 1/2이고 최대 값은 0.111111111 = 1입니다. 이제 다음 플립이 꼬리이면 x는 0.10으로 시작합니다. 가능한 최소값은 0.1000000 = 1/2이고 최대 값은 0.1011111 = 3/4입니다. x의 가능한 최소값은 1/2이므로 x <1/3이 필요하므로 1을 반환 할 가능성이 없습니다. x가 1/2 <x <2/3이면 2를 반환하고 2/3 <x <3/4이면 3을 반환 할 수 있습니다. 이제 세 번째 플립이 꼬리라고 가정하십시오. 그런 다음 x는 0.100으로 시작해야합니다. 최소 = 0.10000000 = 1/2 및 최대 = 0.100111111 = 5/8. 이제 1/3 <1/2 <5/8 <2/3이므로 x는 반드시 구간 (1/3, 2/3)에 속해야하므로 x의 숫자 생성을 중단하고 2 만 반환 할 수 있습니다.
코드는 본질적으로 0과 1 사이의 x를 생성하는 대신 a와 b 사이의 x를 생성하는 것을 제외하고는 이것을 수행하지만 원리는 동일합니다.
def gen(a, b):
min_possible = a
max_possible = b
while True:
floor_min_possible = floor(min_possible)
floor_max_possible = floor(max_possible)
if max_possible.is_integer():
floor_max_possible -= 1
if floor_max_possible == floor_min_possible:
return floor_max_possible
mid = (min_possible + max_possible)/2
if coin_flip():
min_possible = mid
else:
max_possible = mid
비고 : 승인 / 거부 방법과이 두 분포의 균일 분포를 비교하여이 코드를 테스트했습니다. 이 코드는 b-a가 2의 다음 거듭 제곱에 가까운 경우를 제외하고는 거부를 수락하는 것보다 적은 동전 뒤집기가 필요합니다. Ex a = 0, b = 62를 생성하려면 수락 / 거부하는 것이 좋습니다. 이 코드가 평균적으로 수락 / 거부보다 2 번 이상 코인 플립을 사용할 수 있음을 증명할 수있었습니다. 필자가 읽은 바에 따르면 Knuth와 Yao (1976)는이 문제를 해결하는 방법을 제시했으며 그들의 방법이 예상되는 코인 플립 수에서 최적임을 증명했습니다. 그들은 또한 예상되는 플립의 수가 분포의 섀넌 엔트로피보다 커야 함을 증명했습니다. 그러나 논문의 사본을 찾을 수 없었으며 그들의 방법이 무엇인지 궁금합니다. (업데이트 : 방금 1976 년 Knuth Yao의 설명을 찾았습니다.http://www.nrbook.com/devroye/Devroye_files/chapter_fifteen_1.pdf 그러나 아직 읽지 않았습니다). 누군가 가이 스레드에서 Han Hoshi를 언급했는데,이 스레드는보다 일반적으로 보이고 편향 된 동전을 사용하여 해결합니다. 문헌에 대한 자세한 논의는 Pae (2009)의 http://paper.ijcsns.org/07_book/200909/20090930.pdf 를 참조하십시오 .
이것은 b-a가 2 ^ k와 같지 않은 경우에 제안 된 솔루션입니다. 그것은 정해진 수의 단계로 작동해야합니다 (예상 범위를 벗어난 후보자를 버릴 필요가 없습니다).
그러나 나는 이것이 옳지 않다. 이 난수 생성기 (있는 경우)의 정확한 비 균일 성 및 측정 / 정량화 방법을 비판하고 설명하십시오.
먼저 [0, z-1] 범위에서 균일하게 분포 된 난수를 생성하는 등가 문제로 변환합니다. 여기서 z = b-a.
또한 m = 2 ^ k를 2> = z의 가장 작은 거듭 제곱으로합니다.
위의 솔루션에 따라, 우리는 이미 [0, m-1] 범위에 균일하게 분포 된 난수 생성기 R (m)을 가지고 있습니다 (각 비트 당 하나씩 k 코인을 던져서 수행 할 수 있음).
Keep a random seed s and initialize with s = R(m).
function random [0, z-1] :
x = R(m) + s
while x >= z:
x -= z
s = x
return x
while 루프는 최대 3 번 실행되어 고정 된 단계 수로 다음 임의의 숫자를 제공합니다 (최상의 경우 = 최악의 경우).
숫자 [0,2]에 대해서는 테스트 프로그램을 참조하십시오 : http://pastebin.com/zuDD2V6H
이론적으로 최적의 알고리즘
내가 게시 한 다른 답변의 개선 사항은 다음과 같습니다. 다른 대답은 하나의 이산 분포를 다른 것으로부터 생성하는보다 일반적인 경우로 확장하기 쉽다는 이점이 있습니다. 실제로 다른 답변은 Han과 Hoshi로 인한 알고리즘의 특별한 경우입니다.
여기서 설명 할 알고리즘은 Knuth and Yao (1976)를 기반으로합니다. 그들의 논문에서 그들은 또한이 알고리즘이 가능한 최소 코인 플립 수를 달성한다는 것을 증명했습니다.
이를 설명하기 위해 다른 답변에서 설명한 거부 샘플링 방법을 고려하십시오. 예를 들어, 5 개의 숫자 중 하나를 균일하게 생성한다고 가정합니다 [0, 4]. 2의 다음 거듭 제곱은 8이므로 동전을 3 번 뒤집고 최대 8의 난수를 생성합니다. 숫자가 0에서 4이면 반환합니다. 그렇지 않으면 버리고 최대 8까지 다른 숫자를 생성하고 성공할 때까지 다시 시도하십시오. 그러나 당신이 숫자를 버릴 때, 당신은 약간의 엔트로피를 낭비했습니다. 대신에 필요한 동전 코인 플립 수를 줄이기 위해 던져진 수를 조절할 수 있습니다. 구체적으로, 숫자 [0, 7]을 생성 한 후 [0, 4]이면 반환합니다. 그렇지 않으면 5, 6 또는 7이며 각 경우마다 다른 작업을 수행합니다. 5이면 동전을 다시 뒤집고 뒤집기를 기준으로 0 또는 1을 반환하십시오. 6이라면 동전을 뒤집고 2 또는 3을 반환하십시오. 7이면 동전을 뒤집습니다. 머리이면 꼬리가 다시 시작되면 4를 반환하십시오.
초기의 실패한 시도에서 남은 엔트로피는 우리에게 3 가지 사례 (5, 6 또는 7)를 주었다. 우리가 이것을 그냥 던져 버리면 log2 (3) 동전 뒤집기를 버립니다. 우리는 대신 그것을 유지하고 또 다른 뒤집기의 결과와 결합하여 6 가지 가능한 사례 (5H, 5T, 6H, 6T, 7H, 7T)를 생성합니다.이 경우 5/6 확률로 최종 답변을 즉시 다시 시도 할 수 있습니다 5/6 .
코드는 다음과 같습니다.
# returns an int from [0, b)
def __gen(b):
rand_num = 0
num_choices = 1
while True:
num_choices *= 2
rand_num *= 2
if coin.flip():
rand_num += 1
if num_choices >= b:
if rand_num < b:
return rand_num
num_choices -= b
rand_num -= b
# returns an int from [a, b)
def gen(a, b):
return a + __gen(b - a)