programing

범위 내에서 임의의 정수값을 생성하는 방법

goodsources 2022. 8. 16. 23:40
반응형

범위 내에서 임의의 정수값을 생성하는 방법

이것은 이전에 투고된 질문의 후속 조치입니다.

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

다이의 측면을 모방하기 위해 1에서 6까지와 같은 특정 범위 내에서 난수를 생성할 수 있기를 바랍니다.

이걸 어떻게 하면 좋을까요?

지금까지의 답은 모두 수학적으로 틀렸다. ★★★★rand() % N는, 「」의 하고 있지 않습니다.[0, N)~가 아니면N 、 divides 、 divides 、 divides 、 divides divides into into into into into into into divides dividesrand()) 반환(,, 2의 거듭제곱) (즉, 2의 거듭제곱).의 모듈리인지 아닌지를 모른다.rand()독립적이다: 그들이 갈 가능성이 있다.0, 1, 2, ...균일하지만 매우 랜덤하지는 않습니다.하다고 생각되는 은, 「이럴지도 모른다」는 것이다.rand()포아송 분포를 제시합니다. 같은 크기의 겹치지 않는 두 개의 하위 구간이 동등하고 독립적입니다.값의 에서는, 이것은 , 「」의 값이 「」의 값이 합니다.rand()잘 흩어졌네요.

은, 「」의 이, 「」의 범위를 변경하는 것을 의미합니다.rand() 박스로 입니다.★★★★★★★★★★★★★★★★★,RAND_MAX == 11 '어느 정도'의 거죠.1..6 , 을 할당해야 .{0,1} 1 {2,3}2시 정각이 구간은 크기가 같고 분리된 구간이므로 균일하고 독립적으로 분포됩니다.

부동소수점 나눗셈을 사용하는 제안은 수학적으로 타당하지만 원칙적으로 반올림 문제에 시달린다. ★★★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()좋은 있기 에('의에서 알 수 )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)( 값값 ( ( )

범위의 max값과 min값을 알고 있으며 범위 사이에 숫자를 생성하는 경우의 수식은 다음과 같이 입력합니다.

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

@Ryan Reichi.두 번째 경계 검사에서는 첫 번째 경계 검사가 필요하지 않으며 재귀가 아닌 반복 검사로 만들었습니다.됩니다.여기서 [min, max]는 [min, max]입니다.max >= min ★★★★★★★★★★★★★★★★★」1+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);
}

Ryan이 옳지만 랜덤성의 근원에 대해 알려진 바에 따라 솔루션이 훨씬 더 단순해질 수 있습니다.문제를 다시 기술하려면:

  • , 범위내의 합니다.[0, MAX)균일한 분포로
  • 정수 입니다.[rmin, rmax]서 ''는0 <= rmin < rmax < MAX.

제 경험상, 만약 빈(또는 "박스")의 수가 원래 숫자의 범위보다 훨씬 적고, 원래의 소스가 암호학적으로 강력하다면, 모든 rigamarole을 통과할 필요가 없으며, 단순한 모듈로 분할로도 충분합니다(예:output = rnd.next() % (rmax+1) if, if, if, if.rmin == 0속도 저하 없이 균등하게 분포된 난수를 생성합니다.).rand()를 참조해 주세요.

다음은 실제 작동 방식에 대한 예/증명입니다.1부터 22까지의 랜덤 번호를 생성하려고 했습니다.암호화할 수 있는 강력한 소스가 랜덤 바이트(인텔 RDRAND 기반)를 생성하려고 했습니다.결과는 다음과 같습니다.

Rnd distribution test (22 boxes, numbers of entries in each box):     
 1: 409443    4.55%
 2: 408736    4.54%
 3: 408557    4.54%
 4: 409125    4.55%
 5: 408812    4.54%
 6: 409418    4.55%
 7: 408365    4.54%
 8: 407992    4.53%
 9: 409262    4.55%
10: 408112    4.53%
11: 409995    4.56%
12: 409810    4.55%
13: 409638    4.55%
14: 408905    4.54%
15: 408484    4.54%
16: 408211    4.54%
17: 409773    4.55%
18: 409597    4.55%
19: 409727    4.55%
20: 409062    4.55%
21: 409634    4.55%
22: 409342    4.55%   
total: 100.00%

이것은 제 목적에 맞는 균일한 형태입니다(공정 주사위 던지기, http://users.telenet.be/d.rijmenants/en/kl-7sim.htm, 등의 제2차 세계대전 암호 기계용 암호책 생성 등).출력에 현저한 치우침은 없습니다.

다음은 암호학적으로 강력한(진정한) 난수 생성기의 소스입니다.인텔 디지털 난수 생성기 및 64비트(부호 없음) 난수를 생성하는 샘플 코드.

int rdrand64_step(unsigned long long int *therand)
{
  unsigned long long int foo;
  int cf_error_status;

  asm("rdrand %%rax; \
        mov $1,%%edx; \
        cmovae %%rax,%%rdx; \
        mov %%edx,%1; \
        mov %%rax, %0;":"=r"(foo),"=r"(cf_error_status)::"%rax","%rdx");
        *therand = foo;
  return cf_error_status;
}

Mac OS X에서 clang-6.0.1(스트레이트), gcc-4.8.3에서 "-Wa,q" 플래그를 사용하여 컴파일했습니다(GAS는 이러한 새로운 명령을 지원하지 않기 때문입니다).

다음은 Ryan Reich의 솔루션보다 약간 간단한 알고리즘입니다.

/// Begin and end are *inclusive*; => [begin, end]
uint32_t getRandInterval(uint32_t begin, uint32_t end) {
    uint32_t range = (end - begin) + 1;
    uint32_t limit = ((uint64_t)RAND_MAX + 1) - (((uint64_t)RAND_MAX + 1) % range);

    /* Imagine range-sized 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. */
    uint32_t randVal = rand();
    while (randVal >= limit) randVal = rand();

    /// Return the position you hit in the bucket + begin as random number
    return (randVal % range) + begin;
}

Example (RAND_MAX := 16, begin := 2, end := 7)
    => range := 6  (1 + end - begin)
    => limit := 12 (RAND_MAX + 1) - ((RAND_MAX + 1) % range)

The limit is always a multiple of the range,
so we can split it into range-sized buckets:
    Possible-rand-output: 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16
    Buckets:             [0, 1, 2, 3, 4, 5][0, 1, 2, 3, 4, 5][X, X, X, X, X]
    Buckets + begin:     [2, 3, 4, 5, 6, 7][2, 3, 4, 5, 6, 7][X, X, X, X, X]

1st call to rand() => 13
    → 13 is not in the bucket-range anymore (>= limit), while-condition is true
        → retry...
2nd call to rand() => 7
    → 7 is in the bucket-range (< limit), while-condition is false
        → Get the corresponding bucket-value 1 (randVal % range) and add begin
    => 3

하지만 거부 방식의 예측 수 없는 이 으로 덜 합니다.[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);
...

it it 、 it 、 것 、 it 、 it 、 it 、 it 、 it 、 。i * log_2(RAND_MAX + 1)여기서 "bits"(")i 긴 을 합니다.n.

가 트음음음음에 큰 n을 사용하다

RAND_MAX + 1n(이 질문에서와 같이) 또는 2의 거듭제곱이 아닌 경우에는 정수 오버플로를 방지하기 위해 주의해야 합니다.RAND_MAX * n★★★★★★ 。

그냥 하지 않을래?

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

%을 사용하다에서 50까지

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

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

그 외의 옵션에 대해서는, 여기를 참조해 주세요.

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

arc4random_uniform(MAX-MIN)+MIN

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

arc4random_uniform(20-10)+10

arc4random_uniform(10)+10

간단한 해결책으로 "rand() % N"을 사용하는 것보다 낫다.

modulo는 이전에도 말했듯이 분포가 왜곡되어 있기 때문에 충분하지 않습니다.비트를 숨기고 분포가 왜곡되지 않도록 사용하는 내 코드입니다.

static uint32_t randomInRange(uint32_t a,uint32_t b) {
    uint32_t v;
    uint32_t range;
    uint32_t upper;
    uint32_t lower;
    uint32_t mask;

    if(a == b) {
        return a;
    }

    if(a > b) {
        upper = a;
        lower = b;
    } else {
        upper = b;
        lower = a; 
    }

    range = upper - lower;

    mask = 0;
    //XXX calculate range with log and mask? nah, too lazy :).
    while(1) {
        if(mask >= range) {
            break;
        }
        mask = (mask << 1) | 1;
    }


    while(1) {
        v = rand() & mask;
        if(v <= range) {
            return lower + v;
        }
    }

}

다음과 같은 간단한 코드를 사용하여 분포를 확인할 수 있습니다.

int main() {

    unsigned long long int i;


    unsigned int n = 10;
    unsigned int numbers[n];


    for (i = 0; i < n; i++) {
        numbers[i] = 0;
    }

    for (i = 0 ; i < 10000000 ; i++){
        uint32_t rand = random_in_range(0,n - 1);
        if(rand >= n){
            printf("bug: rand out of range %u\n",(unsigned int)rand);
            return 1;
        }
        numbers[rand] += 1;
    }

    for(i = 0; i < n; i++) {
        printf("%u: %u\n",i,numbers[i]);
    }

}

[0,1] 범위의 부동 소수점 숫자를 반환합니다.

#define rand01() (((double)random())/((double)(RAND_MAX)))

언급URL : https://stackoverflow.com/questions/2509679/how-to-generate-a-random-integer-number-from-within-a-range

반응형