주어진 난수 생성기를 사용하여 1-100을 인쇄하는 가장 효율적인 알고리즘


11

RandNum501-50 범위의 균일 한 난수를 생성하는 난수 생성기가 제공됩니다. 이 난수 생성기 만 사용하여 1에서 100까지의 모든 정수를 임의의 순서로 생성하고 인쇄 할 수 있습니다. 모든 숫자는 정확히 한 번만 와야하며 어느 위치에서나 발생할 수있는 확률은 같아야합니다.

가장 효율적인 알고리즘은 무엇입니까?


1
배열 / 비트 벡터를 사용하여 이미 본 숫자를 기록하고 카운터를 사용하여 본 고유 숫자를 기록하십시오.
Dave Clarke

@DaveClarke 어떻게 50보다 큰 숫자를 생성 할 수 있습니까? 한 번 이상 사용하면 어떻게 사용합니까?
Raj Wadhwa

1
물론 모든 장소가 균등 한 가능성으로 발생하도록해야합니다. 사용할 수 있습니다 RandNum100 = (RandNum50() * 2) - (RandNum50 > 25) ? 0 : 1).
Dave Clarke

2
@DaveClarke : 반복 반복 샘플링을 제안 하시겠습니까? 그것은 오직 기대만으로 끝날 것입니다.
라파엘

나는 단지 힌트를주었습니다.
Dave Clarke

답변:


3

Fisher-Yates 셔플 을 사용하는 이 솔루션의 생각 이 잘못되었습니다 . 매번 반복 할 때마다 근사치좋은 균일 한 분포를 유지하려면 (아래 편집 섹션 참조)이 기법을 사용하여 과 사이 의 값을 생성 할 수 있습니다 .0 K - 1O(N2)krand0k1

 // return a random number in [0..k-1] with uniform distribution
 // using a uniform random generator in [1..50]
 funtion krand(k) {    
   sum = 0
   for i = 1 to k do sum = sum + RandNum50() - 1
   krand = sum mod k
 }

Fisher-Yates 알고리즘은 다음과 같습니다.

arr : array[0..99]
for i = 0  to 99 do arr[i] = i+1; // store 1..100 in the array
for i = 99 downto 1 {
  r = krand(i+1)  // random value in [0..i]
  exchange the values of arr[i] and arr[r]
}
for i = 0 to 99 do print arr[i]

편집하다:

Erick이 지적한 것처럼 krand위 의 함수는 실제로 균일 한 분포를 반환하지 않습니다. 더 나은 (임의로 더 나은) 더 빠른 근사치를 얻는 데 사용할 수있는 다른 방법이 있습니다. 하지만 (최대 내 지식) 진정으로 균일 한 분포를 얻을 수있는 유일한 방법은 사용하는 것입니다 거부 샘플링 : 선택 난수 비트와 숫자의 경우 얻을 미만입니다 반환이, 그렇지 않으면 다른 난수를 생성합니다. 가능한 구현 :r km=log2(k)rk

function trulyrand(k) {
    if (k <= 1) return 0
    while (true) { // ... if you're really unlucky ...
      m = ceil(log_2 (k) ) // calculate m such that k < 2^m
      r = 0  // will hold the random value
      while (m >= 0) {  // ... will add m bits        
        if ( rand50() > 25 ) then b = 1 else b = 0   // random bit
        r = r * 2 + b  // shift and add the random bit
        m = m - 1
      }      
      if (r < k) then return r  // we have 0<=r<2^m ; accept it, if r < k
    }
}

1
링크 된 wikipedia 페이지는 변형이 있음을 나타냅니다. O(n)
Dave Clarke

1
여기서 "셔플"이 핵심 용어 인 것 같습니다.
라파엘

4
krand (k)의 트릭은 좋은 근사치이지만 실제로 균일 한 분포를 생성하지는 않습니다. k = 3 인 경우에도 33.333328 %의 확률로 0을 출력 할 수 있습니다. ? 근사치를 원한다면 더 작은 한계가 충분하다고 생각합니다.
Erick Wong

1
@ErickWong : 당신 말이 맞아요. 나는 일정한 균일 한 분포가 일정한 시간 내에 끝나는 것을 보장하지 않는 거부 샘플링 방법을 통해서만 도달 할 수 있다고 생각합니다. 다른 근사 체계가 있습니다 (원하는 근사치에 도달 할 수 있음). 내가 제안한 것은 내 생각에 처음입니다.
Vor

2
r1..100rr

4

다른 사람들이 불확실한 수의 이탈을 취하는 것과 관련된 대략적인 솔루션과 솔루션을 제공했기 때문에 유한 한 횟수의 RandNum50()호출 만 필요로하는 알고리즘이 없다는 증거 는 어떻습니까?

다른 사람들이 지적했듯이, 1-100의 숫자를 임의의 순서로 인쇄하는 것은 이러한 숫자의 임의 순열을 인쇄하는 것과 같습니다. 100이 있습니다! 이러한 순열 중 하나이므로 특정 순열은 확률 로 출력되어야합니다.1100!

kRandNum50kkRandNum50kkRandNum50(r1,r2,,rk)150kc50kc1100!100!50kk100!50k


2

nlogn+O(1)

if ( rand50() > 25 ) then b = 1 else b = 0   // random bit

1n!123n

해당 게시물에서 제안한대로 임의의 비트에서 유니폼을 생성하는 방법을 모르는 경우 이러한 방식으로 유니폼의 근사값을 직접 생성 할 수도 있습니다 (Vor의 "trulyrand"와 동일하지만 더 빠름).

P = (RandNum50()-1) + (RandNum50()-1)*50^1 + (RandNum50()-1)*50^2 + ...

P50PQ=PmodnP > nn=100!P>n


1

나는 확인하기 위해 분석을 수행하지 않은 방법 균일 한 (또는하지 않음)이 될 것, 진정한 셔플로 조정 할 수 있지만, 당신은 단지의 시작 배열에서 선택할 수 i= 일 지수 i + 1(k + RandNum50() + RandNum50() - 1) mod (100 - k)와, 인덱스를 제거, k= 0..99?

이것은 RandNum50() + RandNum50()분포 의 피크를 균일하게 앞으로 "밀어냅니다" .

0 인덱스 (1)을 첫 번째 선택에서 얻을 수 없으며 0을 생성하는 대안 1..50 + 1..50 조정을 신속하게 볼 수 없기 때문에 이것이 언급 한대로 이것이 옳지 않다고 확신합니다. ..99.

최신 정보

내가 언급 한 문제를 해결하기 위해 RandNum100첫 번째 k오프셋 을 무작위로 초기화하기 위해 질문 의견에 언급 된 것처럼 효과적으로 사용 했습니다 .

이것은 정면에 큰 파도가있는 분포를 생성합니다.

1 RandNum50씩 전진하는 대신 다른 것을 사용 하여 먼저 증가 시켰습니다 k. 이것은 나에게 충분한 무작위 결과를 생성하지만 K를 2로 변경하면 쉽게 볼 수 있듯이 여전히 "정확하지 않은"무작위는 아닙니다.

K를 수용 한 VB.NET 코드 테스트 . 실제로 O (K), 6K + 2입니다.

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