1에서 5 사이의 난수를 생성하는 함수가 주어지면 1에서 7 사이의 난수를 생성하는 함수를 작성하십시오.
- 간단한 해결책은 무엇입니까?
- 메모리 사용량을 줄이거 나 더 느린 CPU에서 실행하는 효과적인 솔루션은 무엇입니까?
7 * rand5() / 5
?
1에서 5 사이의 난수를 생성하는 함수가 주어지면 1에서 7 사이의 난수를 생성하는 함수를 작성하십시오.
7 * rand5() / 5
?
답변:
이것은 Adam Rosenfield의 솔루션과 동일하지만 일부 독자에게는 더 분명 할 수 있습니다. rand5 ()는 1에서 5까지의 범위에서 통계적으로 임의의 정수를 리턴하는 함수라고 가정합니다.
int rand7()
{
int vals[5][5] = {
{ 1, 2, 3, 4, 5 },
{ 6, 7, 1, 2, 3 },
{ 4, 5, 6, 7, 1 },
{ 2, 3, 4, 5, 6 },
{ 7, 0, 0, 0, 0 }
};
int result = 0;
while (result == 0)
{
int i = rand5();
int j = rand5();
result = vals[i-1][j-1];
}
return result;
}
어떻게 작동합니까? 이것을 다음과 같이 생각하십시오. 종이에이 2 차원 배열을 인쇄하여 다트 판에 꽂아 무작위로 다트를 던지는 것을 상상해보십시오. 0이 아닌 값에 도달하면 선택할 수있는 0이 아닌 값의 수가 동일하므로 1에서 7 사이의 통계적으로 임의의 값입니다. 당신이 0을 명중하면, 당신이 0이 아닌 때까지 다트를 계속 던지십시오. 이것이 바로이 코드가하는 일입니다. i와 j 인덱스는 다트 판에서 무작위로 위치를 선택하고 좋은 결과를 얻지 못하면 다트를 계속 던집니다.
Adam이 말했듯이 최악의 경우에는 영원히 실행될 수 있지만 통계적으로 최악의 경우는 발생하지 않습니다. :)
rand5
균일 한 경우 vals
그리드의 모든 셀 이 선택 될 확률이 동일합니다. 그리드에는 [1, 7] 간격의 각 정수에 대해 정확히 3 개의 사본과 4 개의 0이 포함됩니다. 따라서 "원시"결과 스트림은 [1, 7] 값과 임의의 개별 허용 값보다 더 빈번하게 발생하는 0을 혼합하는 경향이 있습니다. 그러나 0이 제거되어 [1, 7] 값이 균등하게 혼합되기 때문에 문제가되지 않습니다.
1/7은 밑이 5 인 무한 자릿수이므로 1/7은 일정한 시간에 실행되는 (정확한) 솔루션은 없습니다. 간단한 샘플링 방법은 다음과 같습니다.
int i;
do
{
i = 5 * (rand5() - 1) + rand5(); // i is now uniformly random between 1 and 25
} while(i > 21);
// i is now uniformly random between 1 and 21
return i % 7 + 1; // result is now uniformly random between 1 and 7
이것은 루프의 25/21 = 1.19 반복의 예상 런타임을 갖지만, 영원히 반복 될 가능성은 무한히 작습니다.
N
전화를 걸 수있는 솔루션이 일정 시간 동안 실행되고 있다고 가정 rand5()
합니다. 그런 다음에 대한 호출 순서의 5 ^ N 가능한 결과가 있으며 rand5
각 결과는 1-7입니다. 따라서 출력이 k
각각 1≤k≤7 인 가능한 모든 호출 시퀀스를 합하면 출력 확률 k
은 m / 5 ^ N입니다. 여기서 m은 이러한 시퀀스의 수입니다. 따라서 m / 5 ^ N = 1/7이지만이 ==> 모순에 대한 정수 솔루션 (N, m)은 없습니다.
첫 번째 답변 외에도 다른 답변을 추가하고 싶습니다 . 이 답변은의 사용 횟수를 최대화하기 위해에 대한 호출 rand5()
당 호출 횟수를 최소화하려고 rand7()
합니다. 즉, 임의성을 귀중한 자원으로 생각하면 임의의 비트를 버리지 않고 최대한 많은 리소스를 사용하려고합니다. 이 답변은 Ivan의 답변 에서 제시된 논리와 유사합니다 .
랜덤 변수 의 엔트로피는 잘 정의 된 수량입니다. 확률이 동일한 N 상태 (균일 분포)를 갖는 랜덤 변수의 경우, 엔트로피는 log 2 N입니다. 따라서, rand5()
대략 2.32193 비트의 엔트로피를 rand7()
가지며, 약 2.80735 비트의 엔트로피를 갖습니다. 임의성을 최대한 활용하려면 각 호출에서에 2.32193 비트의 엔트로피를 모두 사용하고에 호출 할 rand5()
때마다 필요한 2.80735 비트의 엔트로피를 적용해야합니다 rand7()
. 그러므로 근본적인 한계는에 대한 호출 rand5()
당 log (7) / log (5) = 1.20906 호출보다 더 나은 것을 할 수 없다는 것 rand7()
입니다.
참고 사항 : 별도로 지정하지 않는 한이 답변의 모든 로그는 2 밑이됩니다. rand5()
는 [0, 4] rand7()
범위의 숫자를 반환한다고 가정하고 [0, 6] 범위의 숫자를 반환한다고 가정합니다. 범위를 각각 [1, 5] 및 [1, 7]로 조정하는 것은 쉽지 않습니다.
그래서 우리는 어떻게합니까? 우리는 0과 1 사이 에서 무한정의 정확한 실수를 생성합니다 (실제로 정확한 수를 계산하고 저장할 수있는 척합니다. 나중에 수정하겠습니다). 우리는베이스 (5)에 그 자리를 생성하여 이러한 숫자를 생성 할 수있다 : 우리는 난수 선택 0 a
1 a
2 a
3 ..., 각 디지트 A를 i
호출하여 선택에게 rand5()
. 예를 들어, RNG가 i
all에 대해 = 1을 선택한 경우 i
매우 무작위 적이 지 않다는 사실을 무시하면 실수 1/5 + 1/5 2 + 1/5 3 + ... = 1/4 (기하학적 계열의 합).
자, 우리는 0과 1 사이의 난수를 선택했습니다. 이제 난수는 균일하게 분포되어 있다고 주장합니다. 직관적으로, 이것은 각 숫자가 균일하게 선택되고 숫자가 무한정 정확하기 때문에 이해하기 쉽습니다. 그러나 이산 분포 대신 연속 분포를 다루고 있기 때문에 이것에 대한 공식적인 증거가 다소 복잡합니다. 따라서 우리는 우리의 숫자가 구간 [ a
, b
] 에있을 확률이 길이와 같음 을 증명해야합니다 그 간격 b - a
. 증거는 독자를위한 연습으로 남겨둔다 =).
이제 [0, 1] 범위에서 균일 한 난수를 선택 했으므로의 출력을 생성하려면 [0, 6] 범위의 일련의 균일 한 난수로 변환해야합니다 rand7()
. 우리는 이것을 어떻게합니까? 우리가 방금했던 것의 반대입니다-우리는 그것을 기초 7에서 무한정 정확한 10 진수로 변환 한 다음, 각각의 기초 7 자리는의 하나의 출력에 해당합니다 rand7()
.
앞에서 예를 들자면 rand5()
무한 스트림 1을 생성하면 임의의 실수는 1/4이됩니다. 1/4을 밑 7로 변환하면 무한 십진수 0.15151515 ...를 얻으므로 출력 1, 5, 1, 5, 1, 5 등으로 생성합니다.
자, 우리는 주된 아이디어를 가지고 있지만 두 가지 문제가 남아 있습니다. 실제로 무한한 정확한 실수를 계산하거나 저장할 수 없기 때문에 유한 부분 만 처리하는 방법은 무엇입니까? 둘째, 우리는 그것을 어떻게 기본 7로 변환합니까?
0과 1 사이의 숫자를 밑수 7로 변환 할 수있는 한 가지 방법은 다음과 같습니다.
무한 정밀도 문제를 해결하기 위해 부분 결과를 계산하고 결과의 상한을 저장합니다. 즉, rand5()
두 번 전화 했고 두 번 모두 1을 반환 했다고 가정 합니다. 지금까지 생성 한 숫자는 0.11 (기본 5)입니다. rand5()
생성 하기 위한 나머지 무한 호출의 나머지가 무엇이든, 우리가 생성하는 임의의 실수는 절대 0.12보다 크지 않습니다. 항상 0.11 ≤ 0.11xyz ... <0.12입니다.
따라서 지금까지의 현재 숫자와 최대 값을 추적하여 두 숫자를 모두 기본 7 로 변환 합니다 . 첫 번째 k
숫자 에 동의 하면 다음 k
숫자를 안전하게 출력 할 수 있습니다 . 기본 5 자리의 무한 스트림은 k
기본 7 표현 의 다음 자리에 영향을 미치지 않습니다 !
그리고 그것은 알고리즘입니다-의 다음 출력을 rand7()
생성 rand5()
하기 위해, 우리는 난수를 기본 7로 변환 할 때 다음 숫자의 값을 확실하게 알 수 있도록 필요한 만큼만 자릿수를 생성합니다 . 테스트 하네스가있는 Python 구현 :
import random
rand5_calls = 0
def rand5():
global rand5_calls
rand5_calls += 1
return random.randint(0, 4)
def rand7_gen():
state = 0
pow5 = 1
pow7 = 7
while True:
if state / pow5 == (state + pow7) / pow5:
result = state / pow5
state = (state - result * pow5) * 7
pow7 *= 7
yield result
else:
state = 5 * state + pow7 * rand5()
pow5 *= 5
if __name__ == '__main__':
r7 = rand7_gen()
N = 10000
x = list(next(r7) for i in range(N))
distr = [x.count(i) for i in range(7)]
expmean = N / 7.0
expstddev = math.sqrt(N * (1.0/7.0) * (6.0/7.0))
print '%d TRIALS' % N
print 'Expected mean: %.1f' % expmean
print 'Expected standard deviation: %.1f' % expstddev
print
print 'DISTRIBUTION:'
for i in range(7):
print '%d: %d (%+.3f stddevs)' % (i, distr[i], (distr[i] - expmean) / expstddev)
print
print 'Calls to rand5: %d (average of %f per call to rand7)' % (rand5_calls, float(rand5_calls) / N)
rand7_gen()
숫자를 기본 7로 변환하는 내부 상태가 있으므로 생성기 를 반환합니다. 테스트 하네스는 next(r7)
10000 번 호출 하여 10000 개의 난수를 생성 한 다음 분포를 측정합니다. 정수 수학 만 사용되므로 결과가 정확합니다.
또한 여기의 숫자는 매우 커지고 매우 빠릅니다. 5와 7의 거듭 제곱이 빠르게 커집니다. 따라서 큰 수의 산술로 인해 많은 난수를 생성 한 후 성능이 눈에 띄게 저하되기 시작합니다. 그러나 여기서 목표는 성능을 최대화하지 않고 임의의 비트 사용을 최대화하는 것이 었습니다 (이차 목표 임에도 불구하고).
한 번의 실행으로 rand5()
10000 번의 호출에 대해 12091 번의 호출을 수행 rand7()
하여 평균 log (7) / log (5) 호출의 최소값을 평균 4 개의 유효 숫자로 달성했으며 결과 출력은 균일했습니다.
포트 위해서 임의의 큰 정수가 내장되어 있지 않은 언어 코드, 당신은의 값을 모자해야 pow5
하고 pow7
네이티브 통합 유형의 최대 값 - 그들은 너무 커질 경우, 다음 재설정 모든 것을 시작합니다. 이렇게하면 호출 rand5()
당 평균 호출 수가 rand7()
매우 약간 증가하지만 32 비트 또는 64 비트 정수의 경우에도 너무 많이 증가하지 않아야합니다.
( Adam Rosenfeld의 답변 을 훔쳐서 약 7 % 빠르게 실행했습니다.)
rand5 ()가 등분 포가있는 {0,1,2,3,4} 중 하나를 리턴하고 목표가 등분 포가있는 {0,1,2,3,4,5,6}을 리턴한다고 가정하십시오.
int rand7() {
i = 5 * rand5() + rand5();
max = 25;
//i is uniform among {0 ... max-1}
while(i < max%7) {
//i is uniform among {0 ... (max%7 - 1)}
i *= 5;
i += rand5(); //i is uniform {0 ... (((max%7)*5) - 1)}
max %= 7;
max *= 5; //once again, i is uniform among {0 ... max-1}
}
return(i%7);
}
루프가 variable에서 만들 수있는 가장 큰 값을 추적합니다 max
. 지금까지의 결과가 max % 7과 max-1 사이이면 결과가 해당 범위에서 균일하게 분산됩니다. 그렇지 않으면 0과 max % 7-1 사이의 임의의 나머지와 rand ()에 대한 또 다른 호출을 사용하여 새 숫자와 새 max를 만듭니다. 그런 다음 다시 시작합니다.
편집 :이 방정식에서 rand5 ()가 x 인 호출 횟수는 x입니다.
x = 2 * 21/25
+ 3 * 4/25 * 14/20
+ 4 * 4/25 * 6/20 * 28/30
+ 5 * 4/25 * 6/20 * 2/30 * 7/10
+ 6 * 4/25 * 6/20 * 2/30 * 3/10 * 14/15
+ (6+x) * 4/25 * 6/20 * 2/30 * 3/10 * 1/15
x = about 2.21 calls to rand5()
5 * rand5() + rand5()
.
연산:
7은 3 비트의 시퀀스로 표현 될 수있다
rand (5)를 사용하여 각 비트를 0 또는 1로 무작위로 채 웁니다.
예를 들어 : rand (5)를 호출하고
결과가 1 또는 2 인
경우 결과가 4 또는 5 인 경우 비트를 0으로 채우고
결과가 3 인 경우 비트를 1로 채우고 무시하고 다시 수행하십시오 (거부)
이 방법으로 3 비트를 0/1로 무작위로 채울 수 있으므로 1-7에서 숫자를 얻을 수 있습니다.
편집 : 이것은 가장 간단하고 가장 효율적인 답변처럼 보입니다. 그래서 여기에 대한 코드가 있습니다.
public static int random_7() {
int returnValue = 0;
while (returnValue == 0) {
for (int i = 1; i <= 3; i++) {
returnValue = (returnValue << 1) + random_5_output_2();
}
}
return returnValue;
}
private static int random_5_output_2() {
while (true) {
int flip = random_5();
if (flip < 3) {
return 0;
}
else if (flip > 3) {
return 1;
}
}
}
int randbit( void )
{
while( 1 )
{
int r = rand5();
if( r <= 4 ) return(r & 1);
}
}
int randint( int nbits )
{
int result = 0;
while( nbits-- )
{
result = (result<<1) | randbit();
}
return( result );
}
int rand7( void )
{
while( 1 )
{
int r = randint( 3 ) + 1;
if( r <= 7 ) return( r );
}
}
rand7() = (rand5()+rand5()+rand5()+rand5()+rand5()+rand5()+rand5())%7+1
편집 : 그것은 작동하지 않습니다. 1000에서 약 2 개의 부품이 사용됩니다 (완벽한 rand5를 가정). 버킷은 다음을 얻습니다.
value Count Error%
1 11158 -0.0035
2 11144 -0.0214
3 11144 -0.0214
4 11158 -0.0035
5 11172 +0.0144
6 11177 +0.0208
7 11172 +0.0144
합으로 전환하여
n Error%
10 +/- 1e-3,
12 +/- 1e-4,
14 +/- 1e-5,
16 +/- 1e-6,
...
28 +/- 3e-11
2를 추가 할 때마다 크기가 커지는 것 같습니다.
BTW : 위의 오류 테이블은 샘플링을 통해 생성되지 않고 다음과 같은 반복 관계에 의해 생성되었습니다.
p[x,n]
에 전화를 걸면output=x
발생할 수 있는 여러 가지 방법 입니다.n
rand5
p[1,1] ... p[5,1] = 1
p[6,1] ... p[7,1] = 0
p[1,n] = p[7,n-1] + p[6,n-1] + p[5,n-1] + p[4,n-1] + p[3,n-1]
p[2,n] = p[1,n-1] + p[7,n-1] + p[6,n-1] + p[5,n-1] + p[4,n-1]
p[3,n] = p[2,n-1] + p[1,n-1] + p[7,n-1] + p[6,n-1] + p[5,n-1]
p[4,n] = p[3,n-1] + p[2,n-1] + p[1,n-1] + p[7,n-1] + p[6,n-1]
p[5,n] = p[4,n-1] + p[3,n-1] + p[2,n-1] + p[1,n-1] + p[7,n-1]
p[6,n] = p[5,n-1] + p[4,n-1] + p[3,n-1] + p[2,n-1] + p[1,n-1]
p[7,n] = p[6,n-1] + p[5,n-1] + p[4,n-1] + p[3,n-1] + p[2,n-1]
int ans = 0;
while (ans == 0)
{
for (int i=0; i<3; i++)
{
while ((r = rand5()) == 3){};
ans += (r < 3) >> i
}
}
ans += (r < 3) << i
다음은 {1, 2, 3, 4, 5}에 균일 분포를 생성하는 난수 생성기를 사용하여 {1, 2, 3, 4, 5, 6, 7}에 균일 분포를 생성합니다. 코드가 지저분하지만 논리가 명확합니다.
public static int random_7(Random rg) {
int returnValue = 0;
while (returnValue == 0) {
for (int i = 1; i <= 3; i++) {
returnValue = (returnValue << 1) + SimulateFairCoin(rg);
}
}
return returnValue;
}
private static int SimulateFairCoin(Random rg) {
while (true) {
int flipOne = random_5_mod_2(rg);
int flipTwo = random_5_mod_2(rg);
if (flipOne == 0 && flipTwo == 1) {
return 0;
}
else if (flipOne == 1 && flipTwo == 0) {
return 1;
}
}
}
private static int random_5_mod_2(Random rg) {
return random_5(rg) % 2;
}
private static int random_5(Random rg) {
return rg.Next(5) + 1;
}
int rand7() {
int value = rand5()
+ rand5() * 2
+ rand5() * 3
+ rand5() * 4
+ rand5() * 5
+ rand5() * 6;
return value%7;
}
선택한 솔루션과 달리 알고리즘은 일정한 시간에 실행됩니다. 그러나 선택한 솔루션의 평균 런타임보다 rand5를 2 번 더 호출합니다.
이 생성기는 완벽하지는 않지만 (0은 다른 숫자보다 확률이 0.0064 % 더 높습니다) 대부분의 실제 목적을 위해 일정 시간 보장은이 부정확성을 능가합니다.
설명
이 솔루션은 숫자 15,624를 7로 나눌 수 있다는 사실에서 파생됩니다. 따라서 0에서 15,624까지의 숫자를 무작위로 균일하게 생성 한 다음 mod 7을 취하면 거의 균일 한 rand7 생성기를 얻을 수 있습니다. 0에서 15,624까지의 숫자는 rand5를 6 번 롤링하고이를 사용하여 다음과 같이 기본 5 숫자의 숫자를 형성함으로써 균일하게 생성 될 수 있습니다.
rand5 * 5^5 + rand5 * 5^4 + rand5 * 5^3 + rand5 * 5^2 + rand5 * 5 + rand5
그러나 mod 7의 속성은 방정식을 약간 단순화 할 수있게합니다.
5^5 = 3 mod 7
5^4 = 2 mod 7
5^3 = 6 mod 7
5^2 = 4 mod 7
5^1 = 5 mod 7
그래서
rand5 * 5^5 + rand5 * 5^4 + rand5 * 5^3 + rand5 * 5^2 + rand5 * 5 + rand5
된다
rand5 * 3 + rand5 * 2 + rand5 * 6 + rand5 * 4 + rand5 * 5 + rand5
이론
숫자 15,624는 무작위로 선택되지 않았지만, fermat의 작은 정리를 사용하여 발견 할 수 있습니다. 이는 p가 소수이면
a^(p-1) = 1 mod p
이것은 우리에게
(5^6)-1 = 0 mod 7
(5 ^ 6) -1은 다음과 같습니다
4 * 5^5 + 4 * 5^4 + 4 * 5^3 + 4 * 5^2 + 4 * 5 + 4
이것은 기본 5 형식의 숫자이므로이 방법을 사용하여 임의의 난수 생성기에서 다른 임의의 난수 생성기로 이동할 수 있습니다. 지수 p-1을 사용할 때 항상 0에 대한 작은 바이어스가 발생합니다.
이 접근법을 일반화하고보다 정확하게하기 위해 다음과 같은 기능을 가질 수 있습니다.
def getRandomconverted(frm, to):
s = 0
for i in range(to):
s += getRandomUniform(frm)*frm**i
mx = 0
for i in range(to):
mx = (to-1)*frm**i
mx = int(mx/to)*to # maximum value till which we can take mod
if s < mx:
return s%to
else:
return getRandomconverted(frm, to)
숙제 문제가 허용됩니까?
이 함수는 "기본 5"수학을 수행하여 0과 6 사이의 숫자를 생성합니다.
function rnd7() {
do {
r1 = rnd5() - 1;
do {
r2=rnd5() - 1;
} while (r2 > 1);
result = r2 * 5 + r1;
} while (result > 6);
return result + 1;
}
가장 효율적인 답변을 제공하려는 추가 제약 조건, 즉 1-5 I
의 길이로 균일하게 분포 된 정수의 입력 스트림, 가장 긴 길이의 1-7에서 균일하게 분포 된 정수로 m
구성된 스트림 O
을 고려하는 경우 에 m
말 L(m)
.
이것을 분석하는 가장 간단한 방법은 스트림 I과 O
5 진수 및 7 자리 숫자를 각각 처리하는 것입니다. 이것은 스트림을 취하고 stream a1, a2, a3,... -> a1+5*a2+5^2*a3+..
과 유사하게 주요 답변의 아이디어에 의해 달성됩니다 O
.
그럼 길이의 입력 스트림의 섹션 가지고 있으면 m choose n s.t. 5^m-7^n=c
어디에 c>0
가능한 한 작게한다. 그때부터 정수로 길이 m의 입력 스트림으로부터 균일 한지도가 1
행 5^m
및 1의 정수에서 다른 균일 맵 7^n
길이의 출력 스트림은 N 당사의 정수에 매핑 될 때 상기 입력 스트림에서 몇 가지 경우를 상실 할 수있는 위치 를 초과 7^n
합니다.
그래서이 값주는 L(m)
주위의 m (log5/log7)
약이다 .82m
.
위의 분석의 어려움은 5^m-7^n=c
정확하게 풀기가 쉽지 않은 방정식 과 균일 한 값 1
이 5^m
초과 7^n
하여 효율성을 잃는 경우 입니다.
문제는 가능한 최상의 m (log5 / log7) 값에 얼마나 근접 할 수 있는지입니다. 예를 들어이 수가 정수에 가까워지면이 정확한 정수의 출력 값을 얻는 방법을 찾을 수 있습니까?
그렇다면 5^m-7^n=c
입력 스트림에서 0
~ 까지 균일 한 난수를 효과적으로 생성하고 (5^m)-1
보다 큰 값을 사용하지 마십시오 7^n
. 그러나 이러한 값은 구출되어 다시 사용할 수 있습니다. 그들은 1에서까지 균일 한 숫자 시퀀스를 효과적으로 생성합니다 5^m-7^n
. 그런 다음이를 사용하여 더 많은 출력 값을 만들 수 있도록 7 진수로 변환 할 수 있습니다.
우리 가 size의 균일 한 입력으로부터 도출 된 정수 T7(X)
의 출력 시퀀스의 평균 길이가되도록 하고 그것을 가정한다면 .random(1-7)
X
5^m=7^n0+7^n1+7^n2+...+7^nr+s, s<7
그런 다음 T7(5^m)=n0x7^n0/5^m + ((5^m-7^n0)/5^m) T7(5^m-7^n0)
확률이 7 ^ n0 / 5 ^ m 인 확률이없는 길이의 시퀀스가 없으므로 길이 5^m-7^n0
가 남습니다 (5^m-7^n0)/5^m)
.
우리가 계속 대체하면 다음을 얻습니다.
T7(5^m) = n0x7^n0/5^m + n1x7^n1/5^m + ... + nrx7^nr/5^m = (n0x7^n0 + n1x7^n1 + ... + nrx7^nr)/5^m
그 후
L(m)=T7(5^m)=(n0x7^n0 + n1x7^n1 + ... + nrx7^nr)/(7^n0+7^n1+7^n2+...+7^nr+s)
이것을 넣는 또 다른 방법은 다음과 같습니다.
If 5^m has 7-ary representation `a0+a1*7 + a2*7^2 + a3*7^3+...+ar*7^r
Then L(m) = (a1*7 + 2a2*7^2 + 3a3*7^3+...+rar*7^r)/(a0+a1*7 + a2*7^2 + a3*7^3+...+ar*7^r)
가장 좋은 경우는 어디 5^m=7^n+s
에서 어디 에서 내 원래의 것 s<7
입니다.
다음 T7(5^m) = nx(7^n)/(7^n+s) = n+o(1) = m (Log5/Log7)+o(1)
과 같이.
최악의 경우는 k와 st 5 ^ m = kx7 + s 만 찾을 수있는 경우입니다.
Then T7(5^m) = 1x(k.7)/(k.7+s) = 1+o(1)
다른 경우는 그 사이에 있습니다. 우리가 매우 큰 m에 대해 얼마나 잘 할 수 있는지, 즉 오류 항을 얼마나 잘 얻을 수 있는지 보는 것이 흥미로울 것입니다.
T7(5^m) = m (Log5/Log7)+e(m)
e(m) = o(1)
일반적 으로 달성하는 것은 불가능 해 보이지만 희망적으로 증명할 수 있습니다 e(m)=o(m)
.
그러면 모든 것은의 5^m
다양한 값에 대한 7 자리 숫자의 분포에 달려 m
있습니다.
나는 이것을 다루는 많은 이론이 있다고 확신한다. 나는 어느 시점에서보고하고보고 할 수있다.
다음은 Adam 's answer의 작동하는 Python 구현입니다 .
import random
def rand5():
return random.randint(1, 5)
def rand7():
while True:
r = 5 * (rand5() - 1) + rand5()
#r is now uniformly random between 1 and 25
if (r <= 21):
break
#result is now uniformly random between 1 and 7
return r % 7 + 1
나는 파이썬으로보고있는 알고리즘을 던져서 그들과 놀 수 있기를 원합니다. 함께 던지기까지 오랜 시간이 걸리지 않았 으면 좋겠다는 희망으로 여기에 게시 할 것이라고 생각했습니다.
왜 간단하지 않습니까?
int random7() {
return random5() + (random5() % 3);
}
이 솔루션에서 1과 7을 얻을 확률은 모듈로 인해 낮습니다. 그러나 빠르고 읽기 쉬운 솔루션을 원한다면이 방법을 사용하십시오.
여기서 rand (n)이 " 0 에서 n-1 까지의 균일 한 분포에서 임의의 정수"를 의미 한다고 가정하면 , 여기에는 Python의 randint를 사용하는 코드 샘플이 있습니다. randint (7) 의 효과를 생성 하기 위해 randint (5) 및 상수 만 사용합니다 . 조금 어리석은 사실
from random import randint
sum = 7
while sum >= 7:
first = randint(0,5)
toadd = 9999
while toadd>1:
toadd = randint(0,5)
if toadd:
sum = first+5
else:
sum = first
assert 7>sum>=0
print sum
do ... while
. , 또는이거나 1보다 큰 숫자 1337
일 수 있습니다 12345
.
Adam Rosenfield의 정답 뒤에는 다음과 같은 전제가 있습니다.
n이 2 인 경우 y = {22, 23, 24, 25}와 같은 4 가지 버리기 가능성이 있습니다. n과 6을 함께 사용하면 1 회만 처리 할 수 있습니다. y = {15625}.
5 ^ 6 = 15625
7 * 2232 = 15624
rand5를 더 많이 호출합니다. 그러나 버리기 값 (또는 무한 루프)을 얻을 가능성이 훨씬 낮습니다. y에 대해 가능한 버리기 값을 얻을 수있는 방법이 없다면 아직 찾지 못했습니다.
내 대답은 다음과 같습니다.
static struct rand_buffer {
unsigned v, count;
} buf2, buf3;
void push (struct rand_buffer *buf, unsigned n, unsigned v)
{
buf->v = buf->v * n + v;
++buf->count;
}
#define PUSH(n, v) push (&buf##n, n, v)
int rand16 (void)
{
int v = buf2.v & 0xf;
buf2.v >>= 4;
buf2.count -= 4;
return v;
}
int rand9 (void)
{
int v = buf3.v % 9;
buf3.v /= 9;
buf3.count -= 2;
return v;
}
int rand7 (void)
{
if (buf3.count >= 2) {
int v = rand9 ();
if (v < 7)
return v % 7 + 1;
PUSH (2, v - 7);
}
for (;;) {
if (buf2.count >= 4) {
int v = rand16 ();
if (v < 14) {
PUSH (2, v / 7);
return v % 7 + 1;
}
PUSH (2, v - 14);
}
// Get a number between 0 & 25
int v = 5 * (rand5 () - 1) + rand5 () - 1;
if (v < 21) {
PUSH (3, v / 7);
return v % 7 + 1;
}
v -= 21;
PUSH (2, v & 1);
PUSH (2, v >> 1);
}
}
다른 것보다 조금 더 복잡하지만 rand5에 대한 호출을 최소화한다고 생각합니다. 다른 솔루션과 마찬가지로 오랫동안 반복 될 가능성은 적습니다.
간단하고 효율적인 :
int rand7 ( void )
{
return 4; // this number has been calculated using
// rand5() and is in the range 1..7
}
( 가장 좋아하는 "프로그래머"만화는 무엇입니까? ) 에서 영감을 얻었습니다 .
선택할 수있는 7 가지 가능성이없는 한 다른 난수를 그려서, 가능성의 수에 5를 곱하십시오. 펄에서 :
$num = 0;
$possibilities = 1;
sub rand7
{
while( $possibilities < 7 )
{
$num = $num * 5 + int(rand(5));
$possibilities *= 5;
}
my $result = $num % 7;
$num = int( $num / 7 );
$possibilities /= 7;
return $result;
}
$possibilities
루프를 종료하고 복귀하려면 항상 25로 커져야합니다. 따라서 첫 번째 결과는 입니다. (실제로 6 [0-124] % 7
이므로) 균일하게 분포되지 않습니다 125 % 7 != 0
.
1부터 시작하는 범위가 마음에 들지 않으므로 0부터 시작합니다 :-)
unsigned rand5()
{
return rand() % 5;
}
unsigned rand7()
{
int r;
do
{
r = rand5();
r = r * 5 + rand5();
r = r * 5 + rand5();
r = r * 5 + rand5();
r = r * 5 + rand5();
r = r * 5 + rand5();
} while (r > 15623);
return r / 2232;
}
from collections import defaultdict def r7(n): if not n: yield [] else: for i in range(1, 6): for j in r7(n-1): yield [i] + j def test_r7(): d = defaultdict(int) for x in r7(6): s = (((((((((x[5] * 5) + x[4]) * 5) + x[3]) * 5) + x[2]) * 5) + x[1]) * 5) + x[0] if s <= 15623: d[s % 7] += 1 print d
나는 그것이 대답되었다는 것을 알고 있지만 이것이 잘 작동하는 것 같지만 그것이 편견이 있는지 말할 수는 없습니다. 내 '테스트'는 그것이 합리적이라고 제안합니다.
아마도 Adam Rosenfield는 논평하기에 충분히 친절 할 것입니까?
내 (순진?) 아이디어는 다음과 같습니다.
rand7을 만들기에 충분한 임의의 비트가있을 때까지 rand5를 축적하십시오. 최대 2 개의 rand5가 필요합니다. rand7 숫자를 얻으려면 누적 값 mod 7을 사용하십시오.
누산기 오버플로를 피하기 위해 누산기가 mod 7이므로 누산기의 mod 7을 사용합니다.
(5a + rand5) % 7 = (k*7 + (5a%7) + rand5) % 7 = ( (5a%7) + rand5) % 7
rand7 () 함수는 다음과 같습니다.
(rand5의 범위를 0-4로하고 rand7도 마찬가지로 0-6입니다.)
int rand7(){
static int a=0;
static int e=0;
int r;
a = a * 5 + rand5();
e = e + 5; // added 5/7ths of a rand7 number
if ( e<7 ){
a = a * 5 + rand5();
e = e + 5; // another 5/7ths
}
r = a % 7;
e = e - 7; // removed a rand7 number
a = a % 7;
return r;
}
편집 : 1 억 회에 대한 결과가 추가되었습니다.
'실제'랜드 기능 모드 5 또는 7
rand5 : 평균 = 1.999802 0 : 20003944 1 : 19999889 2 : 20003690 3 : 19996938 4 : 19995539 rand7 : 평균 = 3.000111 0 : 14282851 1 : 14282879 2 : 14284554 3 : 14288546 4 : 14292388 5 : 14288736 6 : 14280046
내 rand7
평균은 괜찮아 보이고 숫자 분포도 좋아 보입니다.
randt : avg = 3.000080 0 : 14288793 1 : 14280135 2 : 14287848 3 : 14285277 4 : 14286341 5 : 14278663 6 : 14292943
위에서 인용 한 우아한 알고리즘이 있지만 여기에는 접근하는 방법이 있지만 원형 교차로 일 수도 있습니다. 0에서 생성 된 값을 가정합니다.
R2 = 2보다 작은 값을 제공하는 난수 생성기 (샘플 공간 = {0, 1})
R8 = 8보다 작은 값을 제공하는 난수 생성기 (샘플 공간 = {0, 1, 2, 3, 4, 5, 6, 7) })
R2에서 R8을 생성하기 위해 R2를 세 번 실행하고 3 개의 모든 런의 결합 결과를 3 자리의 이진수로 사용합니다. R2가 3 번 실행될 때의 값 범위는 다음과 같습니다.
0 0-> 0
.
.
11 1-> 7
이제 R8에서 R7을 생성하려면 7을 반환하면 R7을 다시 실행하면됩니다.
int R7() {
do {
x = R8();
} while (x > 6)
return x;
}
로터리 솔루션은 R5에서 R2를 생성하는 것입니다 (R8에서 R7을 생성 한 것처럼), R2에서 R8, R8에서 R7을 생성하는 것입니다.
다음은 정수에 완전히 맞고 최적의 약 4 % 내에있는 솔루션입니다 (예 : {0..6}의 모든 숫자에 대해 {0..4}의 1.26 난수 사용). 코드는 스칼라에 있지만 수학은 모든 언어로 명확해야합니다. 7 ^ 9 + 7 ^ 8이 5 ^ 11에 매우 가깝다는 사실을 이용하십시오. 따라서 기본 5에서 11 자리 숫자를 선택한 다음 범위 (9 기본 7 숫자 제공)에 있으면 기본 7에서 9 자리 숫자로 해석하거나 9 자리 숫자를 초과하면 8 자리 숫자로 해석하십시오. . :
abstract class RNG {
def apply(): Int
}
class Random5 extends RNG {
val rng = new scala.util.Random
var count = 0
def apply() = { count += 1 ; rng.nextInt(5) }
}
class FiveSevener(five: RNG) {
val sevens = new Array[Int](9)
var nsevens = 0
val to9 = 40353607;
val to8 = 5764801;
val to7 = 823543;
def loadSevens(value: Int, count: Int) {
nsevens = 0;
var remaining = value;
while (nsevens < count) {
sevens(nsevens) = remaining % 7
remaining /= 7
nsevens += 1
}
}
def loadSevens {
var fivepow11 = 0;
var i=0
while (i<11) { i+=1 ; fivepow11 = five() + fivepow11*5 }
if (fivepow11 < to9) { loadSevens(fivepow11 , 9) ; return }
fivepow11 -= to9
if (fivepow11 < to8) { loadSevens(fivepow11 , 8) ; return }
fivepow11 -= to8
if (fivepow11 < 3*to7) loadSevens(fivepow11 % to7 , 7)
else loadSevens
}
def apply() = {
if (nsevens==0) loadSevens
nsevens -= 1
sevens(nsevens)
}
}
테스트를 인터프리터 (실제로는 REPL)에 붙여 넣으면 다음과 같은 결과가 나타납니다.
scala> val five = new Random5
five: Random5 = Random5@e9c592
scala> val seven = new FiveSevener(five)
seven: FiveSevener = FiveSevener@143c423
scala> val counts = new Array[Int](7)
counts: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0)
scala> var i=0 ; while (i < 100000000) { counts( seven() ) += 1 ; i += 1 }
i: Int = 100000000
scala> counts
res0: Array[Int] = Array(14280662, 14293012, 14281286, 14284836, 14287188,
14289332, 14283684)
scala> five.count
res1: Int = 125902876
분포는 근사하고 평평합니다 (대략 가우시안 분포에서 예상 한대로 각 빈에 10 ^ 8의 1/7의 약 10k 이내).
사용하여 압연 총 , 당신은 둘 다 할 수
이 두 가지 문제는 단순한 rand(5)+rand(5)...
유형 솔루션 의 문제입니다 . 다음 파이썬 코드는 그것을 구현하는 방법을 보여줍니다 (이것은 대부분 배포를 증명합니다).
import random
x = []
for i in range (0,7):
x.append (0)
t = 0
tt = 0
for i in range (0,700000):
########################################
##### qq.py #####
r = int (random.random () * 5)
t = (t + r) % 7
########################################
##### qq_notsogood.py #####
#r = 20
#while r > 6:
#r = int (random.random () * 5)
#r = r + int (random.random () * 5)
#t = r
########################################
x[t] = x[t] + 1
tt = tt + 1
high = x[0]
low = x[0]
for i in range (0,7):
print "%d: %7d %.5f" % (i, x[i], 100.0 * x[i] / tt)
if x[i] < low:
low = x[i]
if x[i] > high:
high = x[i]
diff = high - low
print "Variation = %d (%.5f%%)" % (diff, 100.0 * diff / tt)
그리고이 결과는 다음과 같은 결과를 보여줍니다.
pax$ python qq.py
0: 99908 14.27257
1: 100029 14.28986
2: 100327 14.33243
3: 100395 14.34214
4: 99104 14.15771
5: 99829 14.26129
6: 100408 14.34400
Variation = 1304 (0.18629%)
pax$ python qq.py
0: 99547 14.22100
1: 100229 14.31843
2: 100078 14.29686
3: 99451 14.20729
4: 100284 14.32629
5: 100038 14.29114
6: 100373 14.33900
Variation = 922 (0.13171%)
pax$ python qq.py
0: 100481 14.35443
1: 99188 14.16971
2: 100284 14.32629
3: 100222 14.31743
4: 99960 14.28000
5: 99426 14.20371
6: 100439 14.34843
Variation = 1293 (0.18471%)
rand(5)+rand(5)
이 값이 6을 초과하는 경우를 무시하는 단순한 방법은 위에 표시된 방법의 100 배인 18 %의 일반적인 변형을 갖습니다 .
pax$ python qq_notsogood.py
0: 31756 4.53657
1: 63304 9.04343
2: 95507 13.64386
3: 127825 18.26071
4: 158851 22.69300
5: 127567 18.22386
6: 95190 13.59857
Variation = 127095 (18.15643%)
pax$ python qq_notsogood.py
0: 31792 4.54171
1: 63637 9.09100
2: 95641 13.66300
3: 127627 18.23243
4: 158751 22.67871
5: 126782 18.11171
6: 95770 13.68143
Variation = 126959 (18.13700%)
pax$ python qq_notsogood.py
0: 31955 4.56500
1: 63485 9.06929
2: 94849 13.54986
3: 127737 18.24814
4: 159687 22.81243
5: 127391 18.19871
6: 94896 13.55657
Variation = 127732 (18.24743%)
그리고 Nixuz의 조언에 따라 스크립트를 정리하여 내용을 추출하고 사용할 수 있습니다 rand7...
.
import random
# rand5() returns 0 through 4 inclusive.
def rand5():
return int (random.random () * 5)
# rand7() generator returns 0 through 6 inclusive (using rand5()).
def rand7():
rand7ret = 0
while True:
rand7ret = (rand7ret + rand5()) % 7
yield rand7ret
# Number of test runs.
count = 700000
# Work out distribution.
distrib = [0,0,0,0,0,0,0]
rgen =rand7()
for i in range (0,count):
r = rgen.next()
distrib[r] = distrib[r] + 1
# Print distributions and calculate variation.
high = distrib[0]
low = distrib[0]
for i in range (0,7):
print "%d: %7d %.5f" % (i, distrib[i], 100.0 * distrib[i] / count)
if distrib[i] < low:
low = distrib[i]
if distrib[i] > high:
high = distrib[i]
diff = high - low
print "Variation = %d (%.5f%%)" % (diff, 100.0 * diff / count)
이 답변은 Rand5 함수에서 가능한 가장 많은 엔트로피를 얻는 실험입니다. 따라서 t는 다른 구현보다 다소 불분명하고 거의 확실하게 느립니다.
0-4에서 균일 분포를 가정하고 0-6에서 균일 분포를 가정하면 :
public class SevenFromFive
{
public SevenFromFive()
{
// this outputs a uniform ditribution but for some reason including it
// screws up the output distribution
// open question Why?
this.fifth = new ProbabilityCondensor(5, b => {});
this.eigth = new ProbabilityCondensor(8, AddEntropy);
}
private static Random r = new Random();
private static uint Rand5()
{
return (uint)r.Next(0,5);
}
private class ProbabilityCondensor
{
private readonly int samples;
private int counter;
private int store;
private readonly Action<bool> output;
public ProbabilityCondensor(int chanceOfTrueReciprocal,
Action<bool> output)
{
this.output = output;
this.samples = chanceOfTrueReciprocal - 1;
}
public void Add(bool bit)
{
this.counter++;
if (bit)
this.store++;
if (counter == samples)
{
bool? e;
if (store == 0)
e = false;
else if (store == 1)
e = true;
else
e = null;// discard for now
counter = 0;
store = 0;
if (e.HasValue)
output(e.Value);
}
}
}
ulong buffer = 0;
const ulong Mask = 7UL;
int bitsAvail = 0;
private readonly ProbabilityCondensor fifth;
private readonly ProbabilityCondensor eigth;
private void AddEntropy(bool bit)
{
buffer <<= 1;
if (bit)
buffer |= 1;
bitsAvail++;
}
private void AddTwoBitsEntropy(uint u)
{
buffer <<= 2;
buffer |= (u & 3UL);
bitsAvail += 2;
}
public uint Rand7()
{
uint selection;
do
{
while (bitsAvail < 3)
{
var x = Rand5();
if (x < 4)
{
// put the two low order bits straight in
AddTwoBitsEntropy(x);
fifth.Add(false);
}
else
{
fifth.Add(true);
}
}
// read 3 bits
selection = (uint)((buffer & Mask));
bitsAvail -= 3;
buffer >>= 3;
if (selection == 7)
eigth.Add(true);
else
eigth.Add(false);
}
while (selection == 7);
return selection;
}
}
Rand5에 대한 호출 당 버퍼에 추가 된 비트 수는 현재 4/5 * 2이므로 1.6입니다. 1/5 확률 값이 포함되어 있으면 0.05만큼 증가하므로 1.65이지만 이것을 비활성화 해야하는 코드의 주석을 참조하십시오.
Rand7 호출에 소비 된 비트 = 3 + 1/8 * (3 + 1/8 * (3 + 1/8 * (...
이것은 3 + 3/8 + 3/64 + 3/512 ...) 약 3.42
7 개에서 정보를 추출하여 호출 당 1 / 8 * 1 / 7 비트를 회수하므로 약 0.018
이는 통화 당 3.4 비트의 순 소비량을 제공하며, 이는 Rand7마다 Rand5에 대한 2.125 호출 비율을 의미합니다. 최적은 2.1이어야합니다.
Rand5에 대한 호출 비용이 매우 비싸지 않은 한 (예 : 외부 엔트로피 소스를 호출하는 경우) 이 방법이 다른 많은 방법 보다 훨씬 느리다고 생각합니다 .
PHP에서
function rand1to7() {
do {
$output_value = 0;
for ($i = 0; $i < 28; $i++) {
$output_value += rand1to5();
}
while ($output_value != 140);
$output_value -= 12;
return floor($output_value / 16);
}
루프는 16에서 127 사이의 임의의 숫자를 생성하고 16에서 16으로 나누어 1과 7.9375 사이의 부동 소수점을 생성 한 다음 1과 7 사이의 정수를 얻기 위해 반올림합니다. 실수하지 않으면 16/112 확률이 있습니다. 7 가지 결과 중 하나
extern int r5();
int r7() {
return ((r5() & 0x01) << 2 ) | ((r5() & 0x01) << 1 ) | (r5() & 0x01);
}
7 = 111b
와p(7) = 8 / 125
나는 @Adam Rosenfield와 같은 정확한 솔루션을 제공 하지만 무한 루프 문제 는 없으며 다른 하나는 거의 완벽한 솔루션이지만 첫 번째 솔루션보다 빠른 구현을 제공하는 4 가지 답변이 있다고 생각 합니다.
가장 정확한 솔루션은에 대한 7 번의 호출이 필요 rand5
하지만 이해하기 위해 계속 진행할 수 있습니다.
Adam의 대답의 강점은 완벽한 균일 분포를 제공하며 rand5 ()에 대한 두 번의 호출 만 필요하다는 가능성이 매우 높다는 것입니다 (21/25). 그러나 최악의 경우는 무한 루프입니다.
아래의 첫 번째 솔루션은 완벽한 균일 분포를 제공하지만 총 42 번의 호출이 필요합니다 rand5
. 무한 루프가 없습니다.
다음은 R 구현입니다.
rand5 <- function() sample(1:5,1)
rand7 <- function() (sum(sapply(0:6, function(i) i + rand5() + rand5()*2 + rand5()*3 + rand5()*4 + rand5()*5 + rand5()*6)) %% 7) + 1
R에 익숙하지 않은 사람들을 위해 간단한 버전이 있습니다.
rand7 = function(){
r = 0
for(i in 0:6){
r = r + i + rand5() + rand5()*2 + rand5()*3 + rand5()*4 + rand5()*5 + rand5()*6
}
return r %% 7 + 1
}
의 배포는 rand5
유지됩니다. 수학을 수행하면 루프의 7 개 반복마다 5 ^ 6 개의 가능한 조합이 있으므로 가능한 총 조합 수는 (7 * 5^6) %% 7 = 0
입니다. 따라서 우리는 7의 동일한 그룹으로 생성 된 난수를 나눌 수 있습니다. 이에 대한 자세한 내용은 방법 2를 참조하십시오.
가능한 모든 조합은 다음과 같습니다.
table(apply(expand.grid(c(outer(1:5,0:6,"+")),(1:5)*2,(1:5)*3,(1:5)*4,(1:5)*5,(1:5)*6),1,sum) %% 7 + 1)
1 2 3 4 5 6 7
15625 15625 15625 15625 15625 15625 15625
Adam의 방법이 훨씬 빠르게 실행될 것임을 보여주는 것이 당연하다고 생각합니다. rand5
Adam의 솔루션에서 42 개 이상의 호출이있을 확률 은 매우 작습니다 ( (4/25)^21 ~ 10^(-17)
).
이제 두 번째 방법은 거의 균일하지만 6 번의 호출이 필요합니다 rand5
.
rand7 <- function() (sum(sapply(1:6,function(i) i*rand5())) %% 7) + 1
다음은 간단한 버전입니다.
rand7 = function(){
r = 0
for(i in 1:6){
r = r + i*rand5()
}
return r %% 7 + 1
}
이것은 본질적으로 방법 1의 한 번의 반복입니다. 가능한 모든 조합을 생성하는 경우 결과 카운트는 다음과 같습니다.
table(apply(expand.grid(1:5,(1:5)*2,(1:5)*3,(1:5)*4,(1:5)*5,(1:5)*6),1,sum) %% 7 + 1)
1 2 3 4 5 6 7
2233 2232 2232 2232 2232 2232 2232
5^6 = 15625
시험판 에는 하나의 숫자가 한 번 더 나타납니다 .
이제 방법 1에서 1-6을 더하여 숫자 2233을 각 연속 지점으로 이동합니다. 따라서 총 조합 수가 일치합니다. 이것은 5 ^ 6 %% 7 = 1이기 때문에 작동하며, 7 개의 적절한 변형을 수행하므로 (7 * 5 ^ 6 %% 7 = 0).
방법 1과 2의 인수가 이해되면, 방법 3이 따르고 7 개의 호출 만 필요합니다 rand5
. 이 시점에서 이것이 정확한 솔루션에 필요한 최소 통화 수라고 생각합니다.
다음은 R 구현입니다.
rand5 <- function() sample(1:5,1)
rand7 <- function() (sum(sapply(1:7, function(i) i * rand5())) %% 7) + 1
R에 익숙하지 않은 사람들을 위해 간단한 버전이 있습니다.
rand7 = function(){
r = 0
for(i in 1:7){
r = r + i * rand5()
}
return r %% 7 + 1
}
의 배포는 rand5
유지됩니다. 수학을 수행하면 루프의 7 개 반복 각각에 5 개의 가능한 결과가 있으므로 가능한 총 조합 수는 (7 * 5) %% 7 = 0
입니다. 따라서 동일한 그룹 7에서 생성 된 난수를 나눌 수 있습니다. 이에 대한 자세한 내용은 방법 1과 2를 참조하십시오.
가능한 모든 조합은 다음과 같습니다.
table(apply(expand.grid(0:6,(1:5)),1,sum) %% 7 + 1)
1 2 3 4 5 6 7
5 5 5 5 5 5 5
나는 아담의 방법이 여전히 더 빨리 실행될 것이라는 것을 보여주는 것이 당연하다고 생각합니다. rand5
Adam의 솔루션에서 7 번 이상 호출 할 가능성 은 여전히 작습니다 ( (4/25)^3 ~ 0.004
).
이것은 두 번째 방법의 작은 변형입니다. 거의 균일하지만 7 rand5
번의 호출이 필요합니다 .
rand7 <- function() (rand5() + sum(sapply(1:6,function(i) i*rand5())) %% 7) + 1
다음은 간단한 버전입니다.
rand7 = function(){
r = 0
for(i in 1:6){
r = r + i*rand5()
}
return (r+rand5()) %% 7 + 1
}
가능한 모든 조합을 생성하면 다음과 같은 결과가 나타납니다.
table(apply(expand.grid(1:5,(1:5)*2,(1:5)*3,(1:5)*4,(1:5)*5,(1:5)*6,1:5),1,sum) %% 7 + 1)
1 2 3 4 5 6 7
11160 11161 11161 11161 11161 11161 11160
5^7 = 78125
시험 에서는 두 개의 숫자가 한 번 덜 나타납니다 . 대부분의 경우, 나는 그걸로 살 수 있습니다.
i=7
추가 7*rand5()
하면 모드 7 r
의 값이 변경되지 않으므로 방법 3의 마지막 루프 반복 은 아무런 영향 을 미치지 않습니다 r
.)