Linux에서 rand ()가 Mac보다 더 자주 숫자를 반복하는 이유는 무엇입니까?


87

rand()Linux에서 Mac보다 숫자가 훨씬 자주 반복되는 것으로 보았을 때 작업중 인 프로젝트의 일부로 C에서 해시 맵을 구현하고 무작위 삽입을 사용하여 테스트했습니다 . RAND_MAX두 플랫폼 모두에서 2147483647 / 0x7FFFFFFF입니다. 바이트 배열을 RAND_MAX+1길게하고, RAND_MAX난수를 생성하고 , 각각이 중복인지 메모하고, 표시된대로 목록에서 확인하는 이 테스트 프로그램으로 축소했습니다 .

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

int main() {
    size_t size = ((size_t)RAND_MAX) + 1;
    char *randoms = calloc(size, sizeof(char));
    int dups = 0;
    srand(time(0));
    for (int i = 0; i < RAND_MAX; i++) {
        int r = rand();
        if (randoms[r]) {
            // printf("duplicate at %d\n", r);
            dups++;
        }
        randoms[r] = 1;
    }
    printf("duplicates: %d\n", dups);
}

Linux는 일관 적으로 약 790 백만 개의 복제본을 생성합니다. Mac은 일관되게 하나만 생성하므로 거의 반복하지 않고 생성 할 수있는 모든 난수 를 반복합니다. 누구든지 이것이 어떻게 작동하는지 설명해 주시겠습니까? 매뉴얼 페이지와 다른 것을 말할 수 없으며, 각각의 RNG를 사용하고 있는지, 온라인에서 찾을 수없는 것을 알 수 없습니다. 감사!


4
0..RAND_MAX의 포괄적에서 랜드 () 반환 값 때문에, 배열 요구가 크기 RAND_MAX + 1이 될 수 있습니다
용광로

21
RAND_MAX / e ~ = 790 million 인 것을 알 수 있습니다. 또한 n이 무한대에 접근함에 따라 (1-1 / n) ^ n의 한계는 1 / e입니다.
David Schwartz

3
@DavidSchwartz 내가 당신을 올바르게 이해한다면, Linux에서 그 수가 지속적으로 약 7 억 9 천만 개에 달하는 이유를 설명 할 수 있습니다. 그렇다면 질문은 다음과 같습니다. 왜 / 어떻게 Mac 이 여러 번 반복 되지 않습니까?
Theron S

26
런타임 라이브러리에는 PRNG에 대한 품질 요구 사항이 없습니다. 실제 요구 사항 만 동일한 시드를 사용한 반복성입니다. 분명히 리눅스에서 PRNG의 품질은 Mac보다 낫습니다.
pmg

4
@chux 그렇습니다. 그러나 곱셈을 기반으로하기 때문에 상태는 절대 0이거나 결과 (다음 상태)도 0이 될 수 없습니다. 소스 코드를 기반으로 0으로 시드되면 특수 사례로 0을 확인하지만 시퀀스의 일부로 0을 생성하지는 않습니다.
Arkku

답변:


119

처음에는 macOS rand()가 어떤 식 으로든 숫자를 반복하지 않는 것이 더 좋은 것처럼 들릴지 모르지만 , 생성 된 숫자의 양으로 많은 양의 사본 (실제 약 790 백만 또는 (2 31 -1) 이 보일 것으로 예상 됩니다 ) / e ). 마찬가지로 순서대로 숫자를 반복하면 중복이 생성되지 않지만 매우 무작위로 간주되지는 않습니다. 따라서 Linux 구현은 이 테스트 에서 실제 임의 소스와 구별 할 수 없지만 macOS 는 그렇지 않습니다.rand()rand()

언뜻보기에 놀랍게 보이는 또 다른 것은 macOS rand()가 중복을 피할 수있는 방법 입니다. 소스 코드를 살펴보면 구현은 다음과 같습니다.

/*
 * Compute x = (7^5 * x) mod (2^31 - 1)
 * without overflowing 31 bits:
 *      (2^31 - 1) = 127773 * (7^5) + 2836
 * From "Random number generators: good ones are hard to find",
 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
 * October 1988, p. 1195.
 */
    long hi, lo, x;

    /* Can't be initialized with 0, so use another value. */
    if (*ctx == 0)
        *ctx = 123459876;
    hi = *ctx / 127773;
    lo = *ctx % 127773;
    x = 16807 * lo - 2836 * hi;
    if (x < 0)
        x += 0x7fffffff;
    return ((*ctx = x) % ((unsigned long) RAND_MAX + 1));

이로 RAND_MAX인해 시퀀스가 ​​다시 반복되기 전에 정확히 1과 1 사이의 모든 숫자가 정확히 한 번만 나타납니다. 다음 상태는 곱셈을 기반으로하기 때문에 상태는 절대 0이 될 수 없습니다 (또는 모든 미래 상태도 0이됩니다). 따라서 반복되는 숫자는 첫 번째 숫자이고 0은 반환되지 않는 숫자입니다.

Apple은 적어도 macOS (또는 OS X)가 존재하는 한 문서 및 예제에서 더 나은 난수 생성기의 사용을 장려 해 왔기 때문에 품질이 rand()중요하지 않은 것으로 간주되어 가장 간단한 의사 난수 발생기. (당신이 언급했듯이, 대신 rand()에 사용하는 것이 좋습니다 arc4random().)

관련 메모에서, 무작위성에 대한이 (그리고 다른 많은) 테스트에서 괜찮은 결과를 생성하는 가장 간단한 의사 난수 생성기는 xorshift *입니다 .

uint64_t x = *ctx;
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
*ctx = x;
return (x * 0x2545F4914F6CDD1DUL) >> 33;

이 구현은 테스트에서 거의 정확히 790 백만 개의 복제본을 생성합니다.


5
1980 년대에 발표 된 한 저널 기사 는 "생일 문제"를 기반으로 PRNG에 대한 통계 테스트를 제안했습니다.
pjs

14
"애플은 문서에서 더 나은 난수 생성기의 사용을 장려하고있다"-> 물론 애플은 arc4random()코드 숨김 rand()과 같은 rand()결과를 얻을 수있다 . 프로그래머가 다르게 코딩하도록 유도하는 대신 더 나은 라이브러리 함수를 작성하십시오. "그들은 막 붙어"그들의 선택입니다.
chux-복원 Monica Monica

23
mac에 상수 오프셋 rand()이 없으면 실제 사용에 유용 하지 않기 때문에 rand () % 7이 항상 0을 반환하는 이유는 무엇입니까? , Rand () % 14
phuclv

4
@PeterCordes : rand에 동일한 시드를 사용하여 다시 실행하면 동일한 시퀀스를 생성해야한다는 요구 사항이 있습니다 . OpenBSD rand는 깨 졌으며이 계약에 따르지 않습니다.
R .. GitHub 중지 지원 얼음

8
@ R..GitHubSTOPHELPINGICE rand()동일한 시드를 사용하면 서로 다른 버전의 라이브러리간에 동일한 시퀀스를 생성 해야하는 C 요구 사항이 있습니까? 이러한 보증은 라이브러리 버전 간의 회귀 테스트에 유용 할 수 있지만 C 요구 사항은 없습니다.
chux-복원 Monica Monica

34

MacOS는 stdlib에서 문서화되지 않은 rand () 함수를 제공합니다. 시드하지 않은 경우 출력되는 첫 번째 값은 16807, 282475249, 1622650073, 984943658 및 1144108930입니다. 빠른 검색 은이 시퀀스가 ​​다음 공식을 반복하는 매우 기본적인 LCG 난수 생성기에 해당함을 보여줍니다.

x n +1 = 7 5 · x n (모드 2 31-1 )

이 RNG의 상태는 전적으로 단일 32 비트 정수 값으로 설명되므로주기가 그리 길지 않습니다. 정확히 말하면, 2마다 반복됩니다. (31) 1 내지 2의 모든 값을 출력하는 2 반복 - 31 - 2.

나는 생각하지 않습니다 모든 Linux 버전에 대해 표준 rand () 구현 하지만 자주 사용되는 glibc rand () 함수 가 있습니다. 단일 32 비트 상태 변수 대신 1000 비트가 넘는 풀을 사용하므로 모든 의도와 목적에 따라 완전히 반복되는 시퀀스가 ​​생성되지 않습니다. 다시 말하지만,이 RNG에서 처음 몇 개의 출력을 먼저 시드하지 않고 인쇄하여 사용중인 버전을 찾을 수 있습니다. glibc rand () 함수는 숫자 1804289383, 846930886, 1681692777, 1714636915 및 1957747793을 생성합니다.

따라서 Linux에서 (그리고 MacOS에서는 거의 발생하지 않음) 더 많은 충돌을 일으키는 이유는 rand ()의 Linux 버전이 기본적으로 더 무작위이기 때문입니다.


5
파종 rand()자는 다음과 같이 행동해야합니다srand(1);
pmg

5
rand()macOS 의 소스 코드는 다음 과 같습니다. opensource.apple.com/source/Libc/Libc-1353.11.2/stdlib/FreeBSD/… FWIW, 소스에서 컴파일 된 것과 동일한 테스트를 실행했으며 실제로 결과는 다음과 같습니다. 하나의 복제본. Apple은 arc4random()예제와 문서에서 다른 난수 생성기 (예 : Swift가 인수하기 전) 의 사용을 장려 해 왔 으므로 rand()해당 플랫폼의 기본 앱에서 그 사용이 흔하지 않을 수 있습니다.
Arkku

답장을 보내 주셔서 감사합니다. 그리고 (2 ^ 31) -2의 기간은 왜 내가 관찰 한 것처럼 끝에서 바로 반복되기 시작하는지 설명합니다. 귀하 (@ r3mainer) rand()는 문서화되지 않았지만 @Arkku는 명백한 출처에 대한 링크를 제공했습니다. 내 시스템에서 해당 파일을 찾을 수없는 이유와 int rand(void) __swift_unavailable("Use arc4random instead.");Mac 에서만 볼 수 있는 이유를 알고 stdlib.h있습니까? @Arkku에 연결된 코드가 ... 어떤 라이브러리로 컴파일되었다고 가정합니다.
Theron S

1
@TheronS C 라이브러리 libc로 컴파일됩니다 /usr/lib/libc.dylib. =)
Arkku

5
어떤 버전의 rand()주어진 C 프로그램에서 사용하는 것은은 "컴파일러"또는 "운영 체제"오히려 C 표준 라이브러리의 구현에 의해 결정되지 않는다 (예를 들어, glibc, libc.dylib, msvcrt*.dll).
Peter O.

10

rand()는 C 표준으로 정의되며 C 표준은 사용할 알고리즘을 지정하지 않습니다. 분명히, 애플은 GNU / 리눅스 구현에 열등한 알고리즘을 사용하고있다 : 리눅스는 테스트에서 실제 무작위 소스와 구별 할 수 없지만, 애플 구현은 단지 숫자를 뒤 섞는다.

임의의 품질의 난수를 원하는 경우 반환되는 숫자의 품질을 최소한 보장하는 더 나은 PRNG를 사용하거나 단순히 읽 /dev/urandom거나 유사한 것을 사용하십시오. 후자는 암호화 품질 수치를 제공하지만 속도가 느립니다. 너무 느리더라도 /dev/urandom더 빠른 다른 PRNG에 우수한 씨앗을 제공 할 수 있습니다.


답장을 보내 주셔서 감사합니다. 나는 실제로 좋은 PRNG가 필요하지 않고 해시 맵에 숨어있는 정의되지 않은 동작이 있다고 생각한 다음 그 가능성을 제거하고 플랫폼이 여전히 다르게 동작 할 때 궁금해했습니다.
Theron S

: BTW 여기에 암호 난수 발생기 A를 고정의 예 github.com/divinity76/phpcpp/commit/...는 ..하지만의 C ++ 대신 C와 나는 STL의 구현은 모든 무거운 리프팅을시키는거야 -
hanshenrik

3
@hanshenrik 암호화 RNG는 일반적으로 간단한 해시 테이블에 비해 과도하게 너무 느립니다.
PM 2Ring

1
@ PM2Ring 절대적으로. 해시 테이블 해시는 기본적으로 빠르지 않아야합니다. 그러나 빠르고 빠르지 않고 괜찮은 해시 테이블 알고리즘을 개발하려면 암호화 해시 알고리즘의 몇 가지 트릭을 아는 것이 좋습니다. 가장 빠른 해시 알고리즘에 수수께끼 같은 가장 눈에 띄는 실수를 피하는 데 도움이됩니다. 그럼에도 불구하고, 나는 특정 구현에 대해 광고하지 않았을 것입니다.
cmaster-모니카 복원

@cmaster 충분합니다. 믹싱 함수애벌 런치 효과 와 같은 것들에 대해 조금 아는 것이 좋습니다 . 다행히도 xxhash, murmur3 또는 siphash와 같이 속도를 너무 많이 희생하지 않는 우수한 속성을 가진 비 암호화 해시 함수가 있습니다 (예 : 올바르게 구현 된 경우).
PM 2Ring

5

일반적으로, 랜드 / 랜드 쌍은 결과에서 상위 비트보다 랜덤 성이 적은 하위 비트로 인해 오랫동안 사용되지 않는 것으로 간주되었습니다. 이것은 결과와 관련이있을 수도 있고 아닐 수도 있지만, 일부 랜드 / 랜드 구현이 최신 버전이지만 이전 구현이 계속 유지되고 무작위를 사용하는 것이 좋습니다. ). 내 아치 리눅스 상자에서 다음 노트는 여전히 rand (3) 매뉴얼 페이지에 있습니다.

  The versions of rand() and srand() in the Linux C Library use the  same
   random number generator as random(3) and srandom(3), so the lower-order
   bits should be as random as the higher-order bits.  However,  on  older
   rand()  implementations,  and  on  current implementations on different
   systems, the lower-order bits are much less random than the  higher-or-
   der bits.  Do not use this function in applications intended to be por-
   table when good randomness is needed.  (Use random(3) instead.)

바로 아래 매뉴얼 페이지는 실제로 가장 간단한 LC RNG에 관한 rand 및 srand의 아주 짧고 간단한 예제 구현을 제공하며 작은 RAND_MAX를 갖습니다. C 표준 라이브러리의 내용과 일치한다고 생각하지 않습니다. 또는 적어도 나는 희망하지 않습니다.

일반적으로 표준 라이브러리에서 무언가를 사용하려는 경우 가능한 경우 무작위를 사용하십시오 (man 페이지는 POSIX 표준으로 다시 POSIX.1-2001로 나열하지만 rand는 C가 표준화되기 전에 표준 방식입니다) . 또는 더 나은 방법으로, 열린 Numerical Recipes (또는 온라인에서 찾음) 또는 Knuth를 크랙하여 구현하십시오. 그것들은 정말 쉬우 며 가장 자주 필요한 속성과 알려진 품질을 가진 범용 RNG를 갖기 위해 한 번만하면됩니다.


상황에 감사드립니다. Rust에서는 실제로 고품질 랜덤 성이 필요하지 않으며 MT19937을 구현했습니다. 두 플랫폼이 다르게 동작하는 이유를 찾는 방법에 대해 대부분 궁금했습니다.
테론 S

1
때로는 최선의 질문이 엄격한 필요 대신 단순한 관심으로 요청되는 경우가 있습니다. 특정 호기심의 관점에서 좋은 답변을 얻는 것 같습니다. 당신의 것 중 하나입니다. 모든 호기심 많은 사람들, 진짜 그리고 원래 해커들이 여기 있습니다.
Thomas Kammeyer

rand ()를 개선하는 대신 "rand () 사용을 중지하는 것이 좋습니다. 표준의 어느 것도 특정 발전기라고 말하지 않습니다.
파이프

2
@pipe rand()'더 좋게'만드는 것이 속도를 늦추는 것을 의미한다면 (암호 적으로 안전한 임의의 숫자는 많은 노력이 필요합니다), 조금 더 예측 가능하더라도 빠르게 유지하는 것이 좋습니다. 예를 들어, 우리는 시작에 오랜 시간이 걸리는 생산 응용 프로그램을 가지고 있었고, 엔트로피가 충분히 생성되기를 기다려야하는 초기화가 필요한 RNG를 추적했습니다 ... 보안이 필요하지 않았기 때문에 '나쁜'RNG는 크게 개선되었습니다.
gidds
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.