범위에서 임의의 정수 생성 <-32727, 32727>까지 가능합니다. 시드

주어진 범위 (국경 값 포함)에서 임의의 정수를 생성하는 함수가 필요합니다. 나는 불합리한 품질 / 무작위 요구 사항이 아니며 다음 네 가지 요구 사항이 있습니다.

  • 나는 그것을 빨리해야합니다. 내 프로젝트는 수백만 (또는 때로는 수천만)의 난수를 생성해야하며 현재 생성기 기능은 병목 현상으로 입증되었습니다.
  • 나는 합리적으로 균일해야합니다 (rand () 사용은 완벽하게 좋습니다).
  • 최소-최대 범위는 <0, 1>에서 <-32727, 32727>까지 가능합니다.
  • 시드 가능해야합니다.

현재 다음 C ++ 코드가 있습니다.

output = min + (rand() * (int)(max - min) / RAND_MAX)

문제는 실제로 균일하지 않다는 것입니다 .max는 rand () = RAND_MAX (Visual C ++의 경우 1/32727) 일 때만 반환됩니다. 이것은 마지막 값이 거의 반환되지 않는 <-1, 1>과 같은 작은 범위의 주요 문제입니다.

그래서 펜과 종이를 잡고 다음 공식 ((int) (n + 0.5) 정수 반올림 트릭을 기반으로 함)을 생각해 냈습니다.

여기에 이미지 설명을 입력하십시오

그러나 여전히 나에게 균일 한 분포를 제공하지는 않습니다. 10000 개의 샘플을 반복 실행하면 값 값 -1, 0에 대해 37:50:13의 비율이 표시됩니다.

더 나은 공식을 제안 해 주시겠습니까? (또는 전체 의사 난수 생성기 기능)



답변

귀하의 것보다 빠르지 만 다소 우수하지만 여전히 균등 한 분산 솔루션은

output = min + (rand() % static_cast<int>(max - min + 1))

범위의 크기가 2의 거듭 제곱 인 경우를 제외 하고이 방법은 의 품질에 관계없이 치우친 비 균일 분포 수를 생성합니다rand() . 이 방법의 품질에 대한 종합적인 테스트는 다음을 참조 하십시오 .


답변

가장 간단한 (그리고 최상의) C ++ (2011 표준 사용) 대답은 다음과 같습니다.

#include <random>

std::random_device rd;     // only used once to initialise (seed) engine
std::mt19937 rng(rd());    // random-number engine used (Mersenne-Twister in this case)
std::uniform_int_distribution<int> uni(min,max); // guaranteed unbiased

auto random_integer = uni(rng);

바퀴를 다시 발명 할 필요가 없습니다. 편견에 대해 걱정할 필요가 없습니다. 임의의 시드로 시간을 사용하는 것에 대해 걱정할 필요가 없습니다.


답변

컴파일러가 C ++ 0x를 지원하고이를 사용하는 것이 옵션이라면, 새로운 표준 <random>헤더가 귀하의 요구를 충족시킬 것입니다. uniform_int_distribution최소 및 최대 범위 (필요한 경우 포함)를 허용하는 고품질 을 가지고 있으며 다양한 난수 생성기 중에서 선택하여 해당 분포에 연결할 수 있습니다.

다음은 int[-57, 365]에 균일하게 분포 된 백만 개의 난수를 생성하는 코드입니다 . <chrono>성능이 중요한 관심사라고 언급 할 때 새로운 표준 기능을 사용하여 시간을 측정했습니다.

#include <iostream>
#include <random>
#include <chrono>

int main()
{
    typedef std::chrono::high_resolution_clock Clock;
    typedef std::chrono::duration<double> sec;
    Clock::time_point t0 = Clock::now();
    const int N = 10000000;
    typedef std::minstd_rand G;
    G g;
    typedef std::uniform_int_distribution<> D;
    D d(-57, 365);
    int c = 0;
    for (int i = 0; i < N; ++i) 
        c += d(g);
    Clock::time_point t1 = Clock::now();
    std::cout << N/sec(t1-t0).count() << " random numbers per second.\n";
    return c;
}

나 (2.8GHz Intel Core i5)의 경우 다음과 같이 인쇄됩니다.

초당 2.10268e + 07의 난수.

생성자에 int를 전달하여 생성기를 시드 할 수 있습니다.

    G g(seed);

나중에 int배포에 필요한 범위를 포함하지 않는 것을 발견하면 다음 uniform_int_distribution과 같이 변경하여 해결할 수 있습니다 (예 🙂 long long.

    typedef std::uniform_int_distribution<long long> D;

나중에 minstd_rand고품질 발전기가 충분하지 않다는 것을 알게되면 쉽게 교체 할 수 있습니다. 예 :

    typedef std::mt19937 G;  // Now using mersenne_twister_engine

난수 생성기를 개별적으로 제어하면 난수 분포가 상당히 자유로울 수 있습니다.

또한이 분포의 첫 번째 4 개의 “모멘트”를 사용하여 (사용하지 않음) 계산 하고 분포의 품질을 정량화하기 위해 이론적 값minstd_rand 과 비교했습니다 .

min = -57
max = 365
mean = 154.131
x_mean = 154
var = 14931.9
x_var = 14910.7
skew = -0.00197375
x_skew = 0
kurtosis = -1.20129
x_kurtosis = -1.20001

( x_접두사는 “예상”을 나타냄)


답변

문제를 두 부분으로 나누겠습니다.

  • n0에서 (max-min) 범위 의 난수를 생성하십시오 .
  • 해당 번호에 분을 추가

첫 번째 부분은 분명히 가장 어렵다. rand ()의 반환 값이 완전히 균일하다고 가정 해 봅시다. 모듈로를 사용하면 첫 번째 (RAND_MAX + 1) % (max-min+1)숫자에 편향이 추가 됩니다. 우리가 마법으로 변경 될 가능성이있는 경우에 그래서 RAND_MAXRAND_MAX - (RAND_MAX + 1) % (max-min+1), 더 이상 편견이 없을 것이다.

의사 비결정론을 알고리즘의 실행 시간으로 기꺼이 허용하려는 경우이 직감을 사용할 수 있습니다. rand ()가 너무 큰 숫자를 반환 할 때마다, 우리는 충분히 작은 숫자를 얻을 때까지 다른 임의의 숫자를 요청합니다.

주행 시간은 지금이다 기하학적으로 분산 예상되는 값으로, 첫 번째 시도에 충분히 작은 수를 얻기의 확률이다. 때문에 항상 미만이며 , 우리는 그것을 알고 반복의 예상 수는 항상 적은 어떤 범위이보다 수 있도록. 이 기술을 사용하여 표준 CPU에서 1 초 이내에 수천만 개의 난수를 생성 할 수 있어야합니다.1/ppRAND_MAX - (RAND_MAX + 1) % (max-min+1)(RAND_MAX + 1) / 2p > 1/2

편집하다:

위의 내용은 기술적으로 정확하지만 실제로 DSimon의 답변이 더 유용 할 것입니다. 이 물건을 직접 구현해서는 안됩니다. 나는 거부 샘플링의 많은 구현을 보았고 그것이 올바른지 아닌지를 확인하기가 종종 어렵습니다.


답변

방법에 대한 메르 센 트위스터 ? 부스트 구현은 사용하기가 쉬우 며 많은 실제 응용 프로그램에서 잘 테스트됩니다. 인공 지능 및 진화 알고리즘과 같은 여러 학술 프로젝트에서 직접 사용했습니다.

다음은 6 면체 주사위를 굴리는 간단한 기능을 만드는 예입니다.

#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/variate_generator.hpp>

boost::mt19937 gen;

int roll_die() {
    boost::uniform_int<> dist(1, 6);
    boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist);
    return die();
}

아, 그리고 당신은 당신이 그것을 훨씬 열등하게 사용해야한다고 확신하지 못하는 경우를 대비 하여이 발전기의 포주가 있습니다 rand().

메르 센 트위스터는 마츠모토 마코토와 니시무라 타쿠지가 발명 한 “랜덤 넘버”생성기입니다. 그들의 웹 사이트에는 수많은 알고리즘 구현이 포함되어 있습니다.

본질적으로 Mersenne Twister는 매우 큰 선형 피드백 시프트 레지스터입니다. 이 알고리즘은 19,937 비트 시드에서 작동하며 32 비트 부호없는 정수로 구성된 624 요소 배열에 저장됩니다. 값 2 ^ 19937-1은 메르 센 소수입니다. 시드 조작 기술은 오래된 “트위스트”알고리즘을 기반으로합니다. 따라서 “Mersenne Twister”라는 이름이 사용됩니다.

Mersenne Twister의 매력적인 측면은 숫자를 생성하기 위해 시간이 많이 걸리는 곱셈이 아닌 이진 연산을 사용한다는 것입니다. 이 알고리즘은 기간이 길고 세분성이 우수합니다. 비 암호화 응용 프로그램에 빠르고 효과적입니다.


답변

int RandU(int nMin, int nMax)
{
    return nMin + (int)((double)rand() / (RAND_MAX+1) * (nMax-nMin+1));
}

이것은 32768 정수를 (nMax-nMin + 1) 정수로 매핑합니다. (nMax-nMin + 1)이 작 으면 (필요한 경우) 매핑이 매우 좋습니다. 그러나 (nMax-nMin + 1)이 크면 매핑이 작동하지 않습니다 (예를 들어 32768 값을 같은 확률로 30000 값에 매핑 할 수 없음). 이러한 범위가 필요한 경우 15 비트 rand () 대신 32 비트 또는 64 비트 임의 소스를 사용하거나 범위를 벗어난 rand () 결과를 무시해야합니다.


답변

다음은 숫자를 생성하는 바이어스되지 않은 버전입니다 [low, high].

int r;
do {
  r = rand();
} while (r < ((unsigned int)(RAND_MAX) + 1) % (high + 1 - low));
return r % (high + 1 - low) + low;

범위가 상당히 작은 경우 do루프 에서 비교의 오른쪽을 캐시 할 이유가 없습니다 .