[c] C에서 임의의 int를 생성하는 방법은 무엇입니까?

C에서 임의의 int 숫자를 생성하는 함수가 있습니까? 아니면 타사 라이브러리를 사용해야합니까?



답변

참고 : rand()보안을 위해 사용하지 마십시오 . 암호로 안전한 번호가 필요한 경우이 답변을 참조하십시오 .

#include <time.h>
#include <stdlib.h>

srand(time(NULL));   // Initialization, should only be called once.
int r = rand();      // Returns a pseudo-random integer between 0 and RAND_MAX.

편집 : Linux에서는 random 및 srandom 을 사용하는 것이 좋습니다 .


답변

rand()함수는 <stdlib.h>0과 사이의 의사 난수 정수 를 반환합니다 RAND_MAX. srand(unsigned int seed)시드를 설정하는 데 사용할 수 있습니다 .

다른 범위를 얻기 위해 %연산자를 함께 사용하는 것이 일반적입니다 rand()(이는 균일 성을 다소 떨어 뜨린다는 점을 명심하십시오). 예를 들면 다음과 같습니다.

/* random int between 0 and 19 */
int r = rand() % 20;

균일 성 을 정말로 염려 한다면 다음과 같이 할 수 있습니다.

/* Returns an integer in the range [0, n).
 *
 * Uses rand(), and so is affected-by/affects the same seed.
 */
int randint(int n) {
  if ((n - 1) == RAND_MAX) {
    return rand();
  } else {
    // Supporting larger values for n would requires an even more
    // elaborate implementation that combines multiple calls to rand()
    assert (n <= RAND_MAX)

    // Chop off all of the values that would cause skew...
    int end = RAND_MAX / n; // truncate skew
    assert (end > 0);
    end *= n;

    // ... and ignore results from rand() that fall above that limit.
    // (Worst case the loop condition should succeed 50% of the time,
    // so we can expect to bail out of this loop pretty quickly.)
    int r;
    while ((r = rand()) >= end);

    return r % n;
  }
}


답변

안전한 임의의 문자 또는 정수가 필요한 경우 :

다양한 프로그래밍 언어로 난수를 안전하게 생성하는 방법 에서 설명 했듯이 다음 중 하나를 수행하려고합니다.

예를 들면 다음과 같습니다.

#include "sodium.h"

int foo()
{
    char myString[32];
    uint32_t myInt;

    if (sodium_init() < 0) {
        /* panic! the library couldn't be initialized, it is not safe to use */
        return 1;
    }


    /* myString will be an array of 32 random bytes, not null-terminated */
    randombytes_buf(myString, 32);

    /* myInt will be a random number between 0 and 9 */
    myInt = randombytes_uniform(10);
}

randombytes_uniform() 암호화 적으로 안전하고 편견이 없습니다.


답변

이것을 통해 봅시다. 먼저 srand () 함수를 사용하여 무작위자를 시드합니다. 기본적으로 컴퓨터는 srand ()에 공급되는 숫자를 기반으로 난수를 생성 할 수 있습니다. 동일한 시드 값을 제공하면 매번 동일한 난수가 생성됩니다.

따라서, 항상 변하는 값으로 랜덤 화기를 시드해야합니다. time () 함수를 사용하여 현재 시간의 값을 제공하여이를 수행합니다.

이제 rand ()를 호출하면 매번 새로운 난수가 생성됩니다.

#include <stdio.h>

int random_number(int min_num, int max_num);

int main(void)
{
    printf("Min : 1 Max : 40 %d\n", random_number(1,40));
    printf("Min : 100 Max : 1000 %d\n",random_number(100,1000));
    return 0;
}

int random_number(int min_num, int max_num)
{
    int result = 0, low_num = 0, hi_num = 0;

    if (min_num < max_num)
    {
        low_num = min_num;
        hi_num = max_num + 1; // include max_num in output
    } else {
        low_num = max_num + 1; // include max_num in output
        hi_num = min_num;
    }

    srand(time(NULL));
    result = (rand() % (hi_num - low_num)) + low_num;
    return result;
}


답변

stdlib제공하는 것보다 더 나은 품질의 의사 난수가 필요한 경우 Mersenne Twister를 확인하십시오 . 더 빠릅니다. 예를 들어 샘플 구현이 풍부합니다. 여기 합니다 .


답변

표준 C 함수는 rand()입니다. 솔리테어를위한 카드를 다루기에 충분하지만 끔찍합니다. rand()짧은 숫자 목록을 통한 많은 사이클 구현 과 낮은 비트는 더 짧은 사이클을 갖습니다. 일부 프로그램이 호출하는 방식 rand()은 끔찍하며 전달할 좋은 시드를 계산하는 srand()것은 어렵습니다.

C에서 난수를 생성하는 가장 좋은 방법은 OpenSSL과 같은 타사 라이브러리를 사용하는 것입니다. 예를 들어

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/rand.h>

/* Random integer in [0, limit) */
unsigned int random_uint(unsigned int limit) {
    union {
        unsigned int i;
        unsigned char c[sizeof(unsigned int)];
    } u;

    do {
        if (!RAND_bytes(u.c, sizeof(u.c))) {
            fprintf(stderr, "Can't get random bytes!\n");
            exit(1);
        }
    } while (u.i < (-limit % limit)); /* u.i < (2**size % limit) */
    return u.i % limit;
}

/* Random double in [0.0, 1.0) */
double random_double() {
    union {
        uint64_t i;
        unsigned char c[sizeof(uint64_t)];
    } u;

    if (!RAND_bytes(u.c, sizeof(u.c))) {
        fprintf(stderr, "Can't get random bytes!\n");
        exit(1);
    }
    /* 53 bits / 2**53 */
    return (u.i >> 11) * (1.0/9007199254740992.0);
}

int main() {
    printf("Dice: %d\n", (int)(random_uint(6) + 1));
    printf("Double: %f\n", random_double());
    return 0;
}

왜 그렇게 많은 코드? Java 및 Ruby와 같은 다른 언어에는 임의의 정수 또는 부동 소수점에 대한 기능이 있습니다. OpenSSL은 임의의 바이트 만 제공하므로 Java 또는 Ruby가 정수 또는 부동 소수점으로 변환하는 방법을 모방하려고합니다.

정수의 경우 모듈로 바이어스 를 피하고 싶습니다 . 에서 임의의 4 자리 정수를 가져 rand() % 10000왔지만 rand()Microsoft Windows에서와 같이 0에서 32767까지만 반환 할 수 있다고 가정합니다 . 0에서 2767 사이의 각 숫자는 2768에서 9999 사이의 각 숫자보다 자주 나타납니다. 바이어스를 제거하기 위해 다시 시도 할 수 있습니다.rand() 값이 2768 미만인 동안 2768에서 32767의 30000 값은 0에서 10000 값까지 균일하게 맵핑되기 때문입니다. 9999.

float의 경우 double53 비트의 정밀도를 유지 하기 때문에 53 개의 임의 비트가 필요 합니다 (IEEE 이중이라고 가정). 53 비트 이상을 사용하면 반올림 바이어스가 발생합니다. 일부 프로그래머는와 같은 코드를 작성 rand() / (double)RAND_MAX하지만 rand()Windows에서는 31 비트 또는 15 비트 만 반환 할 수 있습니다.

RAND_bytes()아마도 /dev/urandom리눅스에서 읽음으로써 OpenSSL의 시드 자체 . 임의의 숫자가 많이 필요한 경우 /dev/urandom커널에서 복사해야하므로 모두에서 읽기가 너무 느립니다 . OpenSSL이 시드에서 더 많은 난수를 생성하는 것이 더 빠릅니다.

난수에 대한 추가 정보 :

  • Perl의 Perl_seed () 는 C에서 시드를 계산하는 방법의 예입니다 srand(). 읽을 수없는 경우 현재 시간의 비트, 프로세스 ID 및 일부 포인터를 혼합 /dev/urandom합니다.
  • OpenBSD의 arc4random_uniform ()은 모듈로 바이어스를 설명합니다.
  • java.util.Random 용 Java API 는 임의의 정수에서 바이어스를 제거하고 53 비트를 임의의 부동 소수점으로 패킹하는 알고리즘을 설명합니다.

답변

시스템 arc4random이 기능 군을 지원하는 경우 표준 rand기능 대신 해당 기능을 사용하는 것이 좋습니다 .

arc4random가족이 포함됩니다 :

uint32_t arc4random(void)
void arc4random_buf(void *buf, size_t bytes)
uint32_t arc4random_uniform(uint32_t limit)
void arc4random_stir(void)
void arc4random_addrandom(unsigned char *dat, int datlen)

arc4random 임의의 32 비트 부호없는 정수를 반환합니다.

arc4random_buf매개 변수에 임의의 내용을 넣습니다 buf : void *. 내용의 양은 bytes : size_t매개 변수에 의해 결정됩니다 .

arc4random_uniform는 규칙을 따르는 임의의 32 비트 부호없는 정수를 반환합니다. 0 <= arc4random_uniform(limit) < limit여기서 limit은 부호없는 32 비트 정수입니다.

arc4random_stir내부 난수 풀을 추가로 무작위 화 /dev/urandom하기 arc4random_addrandom위해 데이터를 읽고 전달합니다 .

arc4random_addrandomarc4random_stir전달 된 데이터에 따라 내부 난수 풀을 채우는 데 사용됩니다 .

이러한 기능이 없지만 Unix에있는 경우이 코드를 사용할 수 있습니다.

/* This is C, not C++ */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h> /* exit */
#include <stdio.h> /* printf */

int urandom_fd = -2;

void urandom_init() {
  urandom_fd = open("/dev/urandom", O_RDONLY);

  if (urandom_fd == -1) {
    int errsv = urandom_fd;
    printf("Error opening [/dev/urandom]: %i\n", errsv);
    exit(1);
  }
}

unsigned long urandom() {
  unsigned long buf_impl;
  unsigned long *buf = &buf_impl;

  if (urandom_fd == -2) {
    urandom_init();
  }

  /* Read 4 bytes, or 32 bits into *buf, which points to buf_impl */
  read(urandom_fd, buf, sizeof(long));
  return buf_impl;
}

urandom_init함수는 /dev/urandom장치를 열고 파일 설명자를에 넣습니다 urandom_fd.

urandom함수는 기본적으로에 대한 호출과 동일 rand하지만 더 안전합니다.long (쉽게 변경 가능)을 .

그러나 /dev/urandom조금 느려질 수 있으므로 다른 난수 생성기의 시드로 사용하는 것이 좋습니다.

시스템이없는 경우 /dev/urandom, 그러나 않습니다/dev/random또는 유사한 파일을, 당신은 단순히 전달 경로를 변경할 수 open있는을 urandom_init. POSIX와 호환 urandom_init되고 사용되는 호출 및 API urandom는 POSIX 호환 시스템이 아니라면 대부분 작동합니다.

참고 : /dev/urandom사용 가능한 엔트로피가 충분하지 않은 경우 읽은 내용은 차단되지 않으므로 이러한 상황에서 생성 된 값은 암호화 적으로 안전하지 않을 수 있습니다. 걱정 /dev/random이되는 경우 엔트로피가 충분하지 않으면 항상 차단하는를 사용하십시오.

다른 시스템 (예 : Windows)을 사용하는 경우 rand또는 일부 내부 Windows 특정 플랫폼 종속 비 휴대용 API를 사용하십시오.

함수에 대한 래퍼 urandom, rand또는 arc4random전화 :

#define RAND_IMPL /* urandom(see large code block) | rand | arc4random */

int myRandom(int bottom, int top){
    return (RAND_IMPL() % (top - bottom)) + bottom;
}