[c] 범위 내에서 임의의 정수를 생성하는 방법

이것은 이전에 게시 된 질문의 후속입니다.

C에서 난수를 생성하는 방법은 무엇입니까?

주사위의 측면을 모방하기 위해 1 ~ 6과 같은 특정 범위 내에서 임의의 숫자를 생성 할 수 있기를 바랍니다.

어떻게하면 되나요?



답변

지금까지의 모든 답은 수학적으로 잘못되었습니다. 반환 하는 구간의 길이를 나누지 않는 한 (즉, 2의 거듭 제곱) 반환 rand() % N은 범위 내의 숫자를 균일하게 제공하지 않습니다 . 또한의 모듈 리가 독립적 인지 여부를 알지 못합니다. 균일하지만 매우 무작위 적이 지 않은. 합리적으로 보이는 유일한 가정 은 포아송 분포를내는 것입니다. 같은 크기의 겹치지 않는 두 부분 구간은 똑같이 가능성이 있고 독립적입니다. 유한 한 값 집합의 경우 이는 균일 한 분포를 의미하며의 값 이 잘 분산되도록합니다.[0, N)Nrand()rand()0, 1, 2, ...rand()rand()

즉, 범위를 변경하는 유일한 올바른 방법은 rand()상자로 나누는 것입니다. 예를 들어 RAND_MAX == 11범위를 원하는 경우 1, 2 등으로 1..6할당해야합니다 . 이들은 분리되고 동일한 크기의 간격이므로 균일하고 독립적으로 분포됩니다.{0,1}{2,3}

부동 소수점 나누기를 사용하라는 제안은 수학적으로 타당하지만 원칙적으로 반올림 문제가 있습니다. 아마도 double그것을 작동시키기에 충분히 높은 정밀도 일 것입니다. 아마 아닐거야. 나는 모르고 그것을 알아 내고 싶지도 않다. 어쨌든 대답은 시스템에 따라 다릅니다.

올바른 방법은 정수 산술을 사용하는 것입니다. 즉, 다음과 같은 것을 원합니다.

#include <stdlib.h> // For random(), RAND_MAX

// Assumes 0 <= max <= RAND_MAX
// Returns in the closed interval [0, max]
long random_at_most(long max) {
  unsigned long
    // max <= RAND_MAX < ULONG_MAX, so this is okay.
    num_bins = (unsigned long) max + 1,
    num_rand = (unsigned long) RAND_MAX + 1,
    bin_size = num_rand / num_bins,
    defect   = num_rand % num_bins;

  long x;
  do {
   x = random();
  }
  // This is carefully written not to overflow
  while (num_rand - defect <= (unsigned long)x);

  // Truncated division is intentional
  return x/bin_size;
}

루프는 완벽하게 균일 한 분포를 얻기 위해 필요합니다. 예를 들어, 0에서 2까지의 임의의 숫자가 주어지고 0에서 1까지의 숫자 만 원하면 2를 얻지 못할 때까지 계속 당기십시오. 이것이 동일한 확률로 0 또는 1을 제공하는지 확인하는 것은 어렵지 않습니다. 이 방법은 다르게 코딩되었지만 nos가 대답 한 링크에도 설명되어 있습니다. 나는 더 나은 배포판을 가지고 random()있기보다는 rand()(에 대한 man 페이지에서 언급했듯이 rand()) 사용하고 있습니다.

기본 범위를 벗어난 임의의 값을 얻으려면 [0, RAND_MAX]까다로운 작업을 수행해야합니다. 아마도 가장 편리한 함수를 정의하는 것입니다 random_extended()끌어 n비트 (사용 random_at_most()으로) 되돌아 [0, 2**n)한 다음 적용 random_at_most()random_extended()대신에 random()(그리고 2**n - 1대신에 RAND_MAX) 이하의 임의의 값을 뽑아 2**n당신이 그런를 보유 할 수있는 숫자 유형이 가정, 가치. 마지막으로, 물론 음수 값을 포함 [min, max]하여를 사용하여 값을 얻을 수 있습니다 min + random_at_most(max - min).


답변

@Ryan Reich의 답변에 이어 정리 된 버전을 제공 할 것이라고 생각했습니다. 첫 번째 경계 검사는 두 번째 경계 검사를 고려할 때 필요하지 않으며 재귀 적이 아닌 반복적으로 만들었습니다. [최소, 최대] 범위의 값을 반환합니다 . 여기서 max >= min1+max-min < RAND_MAX.

unsigned int rand_interval(unsigned int min, unsigned int max)
{
    int r;
    const unsigned int range = 1 + max - min;
    const unsigned int buckets = RAND_MAX / range;
    const unsigned int limit = buckets * range;

    /* Create equal size buckets all in a row, then fire randomly towards
     * the buckets until you land in one of them. All buckets are equally
     * likely. If you land off the end of the line of buckets, try again. */
    do
    {
        r = rand();
    } while (r >= limit);

    return min + (r / buckets);
}


답변

다음은 범위의 최대 값과 최소값을 알고 있고 범위 사이에 포함 된 숫자를 생성하려는 경우 공식입니다.

r = (rand() % (max + 1 - min)) + min


답변

unsigned int
randr(unsigned int min, unsigned int max)
{
       double scaled = (double)rand()/RAND_MAX;

       return (max - min +1)*scaled + min;
}

다른 옵션 은 여기 를 참조 하십시오 .


답변

그냥하지 않겠습니까?

srand(time(NULL));
int r = ( rand() % 6 ) + 1;

%모듈러스 연산자입니다. 기본적으로 6으로 나누고 나머지를 반환합니다 … from 0-5


답변

편향 문제를 이해하고 있지만 거부 기반 방법의 예측할 수없는 런타임을 견딜 수없는 사람들을 위해이 시리즈는 [0, n-1]간격 에서 점차적으로 편향된 임의의 정수를 생성 합니다.

r = n / 2;
r = (rand() * n + r) / (RAND_MAX + 1);
r = (rand() * n + r) / (RAND_MAX + 1);
r = (rand() * n + r) / (RAND_MAX + 1);
...

고정 소수점 임의의 i * log_2(RAND_MAX + 1)비트 수 ( i반복 횟수) 를 합성하고에 의해 긴 곱셈을 수행하여이를 수행합니다 n.

에 비해 비트 수가 충분히 크면 n바이어스가 헤아릴 수 없을 정도로 작아집니다.

이 질문 에서와 같이 RAND_MAX + 1이보다 작은 지 n( 이 질문 에서와 같이 ) 또는 2의 거듭 제곱 아닌지 여부 는 중요하지 않지만 RAND_MAX * n큰 경우 정수 오버플로를 방지하려면주의해야합니다 .


답변

모듈로 편향을 피하기 위해 (다른 답변에서 제 안됨) 항상 다음을 사용할 수 있습니다.

arc4random_uniform(MAX-MIN)+MIN

여기서 “MAX”는 상한이고 “MIN”은 하한입니다. 예를 들어 10에서 20 사이의 숫자의 경우 :

arc4random_uniform(20-10)+10

arc4random_uniform(10)+10

간단한 솔루션이며 “rand () % N”을 사용하는 것보다 낫습니다.