ProgramingTip

C ++에서 0과 1 사이에 발생하게 만드는 임의의 이중을 어떻게 생성합니까?

bestdevel 2020. 12. 8. 19:20
반응형

C ++에서 0과 1 사이에 발생하게 만드는 임의의 이중을 어떻게 생성합니까?


C ++에서 0과 1 사이에 발생하게 만드는 임의의 이중을 어떻게 생성합니까?

물론 몇 가지 대답을 생각할 수있는 표준 관행이 무엇인지 알고 싶습니다.

  • 좋은 표준 준수
  • 좋은 임의성
  • 좋은 속도

(내 응용 프로그램의 경우 속도가 임의성보다 중요합니다).

감사합니다!

추신 : 중요한 경우, 내 오리지날 플랫폼은 Linux와 Windows입니다.


C ++ 11 및 C ++ 14에서는 임의 헤더에 훨씬 더 나은 옵션이 있습니다 . 테이션 유해 고려 랜드 () 에 의해 스테판 T. Lavavej는 우리가 사용하는 피하다해야하는 이유를 설명하는 rand()찬성 ++ C 에서을 random헤더와 14 C ++로 깎아 랜드 () : N3924 더 보강하는이 시점.

아래 예제는 cppreference 사이트에있는 샘플 코드의 수정 된 버전이며 std :: mersenne_twister_engine 엔진과 std :: uniform_real_distribution 을 사용하여 [0,1)범위 내 에서 숫자 를 생성합니다 ( 라이브 참조 ).

#include <iostream>
#include <iomanip>
#include <map>
#include <random>

int main()
{
    std::random_device rd;


    std::mt19937 e2(rd());

    std::uniform_real_distribution<> dist(0, 1);

    std::map<int, int> hist;
    for (int n = 0; n < 10000; ++n) {
        ++hist[std::round(dist(e2))];
    }

    for (auto p : hist) {
        std::cout << std::fixed << std::setprecision(1) << std::setw(2)
                  << p.first << ' ' << std::string(p.second/200, '*') << '\n';
    }
}

출력은 다음과 유사합니다.

0 ************************
1 *************************

게시물에서 속도가 중요하다고 언급했거나 많은 난수 엔진을 설명하는 cppreference 섹션을 언급합니다 ( 강조 내 ).

어떤 엔진을 사용할 것인지 선택하는 데는 여러 장단점이 있습니다 *. ** 선형 합동 엔진은 적당히 빠르며 상태에 대한 저장 요구 사항이 매우 적습니다. 지연된 피보나치 낮은 수준의 고급 산술 명령없이 프로세서에 매우 빨리 큰 상태 저장 비용 절감 느리고 더 큰 상태 저장 요구 사항 메르 센 트위스터 하지만 오른쪽 우선 변수 (바람직 주어진 정의) 가장 바람직한 스펙트럼 특성과 긴 비 반복 시퀀스를 나타냅니다.

그래서 욕구가있을 경우에는 아마도 ranlux24_base 또는 ranlux48_base 을 통해 더 나은 선택이다 mt19937은 .

랜드 ()

rand()강제 로 사용 생성 소수 소수 난수사용 합니까? 에 대한 가이드에 대한 C FAQ 를 참조하십시오 . , 간격에 대해 생성하는 것과 같은 제안을 제공합니다 [0,1).

#include <stdlib.h>

double randZeroToOne()
{
    return rand() / (RAND_MAX + 1.);
}

다음 범위에서 난수를 생성합니다 [M,N).

double randMToN(double M, double N)
{
    return M + (rand() / ( RAND_MAX / (N-M) ) ) ;  
}

다음과 같은 구식 솔루션 :

double X=((double)rand()/(double)RAND_MAX);

모든 기준 (휴대용, 표준 및 고속)을 반드시해야합니다. 생성 된 난수는 표준 절차에 따라 시드되어야합니다.

srand((unsigned)time(NULL));

Boost 랜덤 라이브러리random_real 클래스 가 필요 합니다.


다음은 C ++ TR1 을 사용하는 경우 수행하는 방법 입니다 .


속도가 주된 관심사라면 간단히

double r = (double)rand() / (double)RAND_MAX;

C ++ 11 표준 라이브러리에는 괜찮은 프레임 워크와 몇 가지 서비스 가능 생성기가 포함되어있어 숙제를 할당하고 바로 사용할 수 있습니다.

그러나 모든 생성기주의 사항이 사용하기 전에 다양한 생성기의 특정 속성이 무엇인지 정확히 알아야합니다. 또한 넉 넉넉한 럭셔리 요소와 함께 사용하는 경우 ranlux 생성기를 제외하고는 TestU01과 같은 PRNG에 대한 표준 테스트를 통과하지.

견고하고 반복 가능한 결과를 자신의 저것을 가져와야합니다.

이식성 자체 생성기를 가져와야합니다.

이식성으로 살 수있는 권한 부여 워크 스테이션 또는 C ++ 11 프레임 워크를 자체 생성기와 함께 사용할 수 있습니다.

우수한 품질과 링크의 간단하면서도 높은 수준의 내용에 대한 생성기 코드가 있습니다.

전문적인 경우에는 두 가지 문제가 더 있습니다.

  • 개방 vs. 반 개방 vs. 폐쇄 범위, 즉 (0,1), [0, 1) 또는 [0,1]
  • 적분에서 부동 소수점으로 변환하는 방법 (정밀도, 속도)

변환 방법은 0과 1의 포함 / 제외를 처리 둘 다 동일한 동전입니다. 다음은 반 개방 구간에 대한 세 가지 다른 방법입니다.

// exact values computed with bc

#define POW2_M32   2.3283064365386962890625e-010
#define POW2_M64   5.421010862427522170037264004349e-020

double random_double_a ()
{
   double lo = random_uint32() * POW2_M64;
   return lo + random_uint32() * POW2_M32;
}

double random_double_b ()
{
   return random_uint64() * POW2_M64;
}

double random_double_c ()
{
   return int64_t(random_uint64()) * POW2_M64 + 0.5;
}

( random_uint32()random_uint64()실제 기능을위한 자리이며, 일반적으로 템플릿 매개 변수로 전달 될 것이다)

방법 a 는 더 낮은 값에 대해 설명 하는 방법 에 의해 편향되지 않는 방법을 보여줍니다. 64 비트 용 코드는 더 간단하고 11 비트를 마스킹하는 것으로 표시되지 않습니다. (다양한 ulp로 인한 그리드 간격 증가)는 모든 함수에 대해 실제하지만이 트릭이 다른 곳보다 0에 가까운 영역에 더 많은 다른 값이있을 것입니다.

방법 c 는 FPU가 부호있는 64 비트 정수 유형 만 알고있는 인기있는 특정 플랫폼에서 방법을 보여줍니다. 가장 자주 보는 방법 b 이지만 컴파일러는 서명되지 않은 의미 체계를 유지하기 위해 후드 아래에 많은 추가 코드를 생성해야합니다.

굳이 기본을 혼합하고 일치시켜 나만의 솔루션을 만드십시오.

이 모든 것은 위르겐 Doornik의 우수한 논문 변환 높은 기간 임의의 숫자의 부동 소수점에 대한 설명에서 합니다.


먼저 stdlib.h 포함

#include<stdlib.h>

다음은 C 프로그래밍 언어의 범위 사이에서 임의의 이중 숫자를 생성하는 함수가 될 수 있습니다.

double randomDouble() {
    double lowerRange = 1.0;
    double upperRange = 10.0;
    return ((double)rand() * (upperRange - lowerRange)) / (double)RAND_MAX + lowerRange;
}

여기서 RAND_MAX는 stdlib.h에 정의되어 있습니다.


보시다시피 세 가지 방법이 있습니다.

1) 쉬운 방법.

double rand_easy(void)
{       return (double) rand() / (RAND_MAX + 1.0);
}

2) 안전한 방법 (표준 준수).

double rand_safe(void)
{
        double limit = pow(2.0, DBL_MANT_DIG);
        double denom = RAND_MAX + 1.0;
        double denom_to_k = 1.0;
        double numer = 0.0;

        for ( ; denom_to_k < limit; denom_to_k *= denom )
           numer += rand() * denom_to_k;

        double result = numer / denom_to_k;
        if (result == 1.0)
           result -= DBL_EPSILON/2;
        assert(result != 1.0);
        return result;
}

3) 사용자 정의 방법.

제거 많은 rand()우리는 더 이상 특정 버전의 특이성에 대해 걱정할 필요가있는 자체 구현에서 여유를 확보합니다.

참고 : 여기에 사용 된입니다.의주기는 ≅ 1.8e + 19.

#define RANDMAX (-1ULL)
uint64_t custom_lcg(uint_fast64_t* next)
{       return *next = *next * 2862933555777941757ULL + 3037000493ULL;
}

uint_fast64_t internal_next;
void seed_fast(uint64_t seed)
{       internal_next = seed;
}

double rand_fast(void)
{
#define SHR_BIT (64 - (DBL_MANT_DIG-1))
        union {
            double f; uint64_t i;
        } u;
        u.f = 1.0;
        u.i = u.i | (custom_lcg(&internal_next) >> SHR_BIT);
        return u.f - 1.0;
}

어떤 선택을하든 다음과 같이 기능을 확장 할 수 있습니다.

double rand_dist(double min, double max)
{       return rand_fast() * (max - min) + min;
}

double rand_open(void)
{       return rand_dist(DBL_EPSILON, 1.0);
}

double rand_closed(void)
{       return rand_dist(0.0, 1.0 + DBL_EPSILON);
}

최종 참고 사항 : C로 빠른 버전은 C ++에서 사용하는 조정 된 값 수 std::generate_canonical있는 충분한 유효 비트가있는 것을 방출하는 생성기에서 작동합니다.

64 비트 생성기는 전체 너비를 사용하여 수정 (시프트 조정)없이 사용할 수 있습니다. 를 들어 이것은 예 std::mt19937_64엔진 에서있는 그대로 작동합니다.


단순성과 속도를 기본 기준으로 고려하면 다음과 같은 작은 일반 도우미를 추가 할 수 있습니다.

  // C++ rand generates random numbers between 0 and RAND_MAX. This is quite a big range
  // Normally one would want the generated random number within a range to be really
  // useful. So the arguments have default values which can be overridden by the caller
  int nextRandomNum(int low = 0, int high = 100) const {
    int range = (high - low) + 1;
    // this modulo operation does not generate a truly uniformly distributed random number
    // in the span (since in most cases lower numbers are slightly more likely), 
    // but it is generally a good approximation for short spans. Use it if essential
    //int res = ( std::rand() % high + low );
    int res = low + static_cast<int>( ( range * std::rand() / ( RAND_MAX + 1.0) ) );
    return res;
  }

난수 생성은 잘 연구되고 복잡하며 고급 주제입니다. 다른 답변에서 언급 한 것 외에도 간단하지만 유용한 알고리즘을 찾을 수 있습니다.

영원한 혼란


Mersenne Twister 알고리즘을 사용해 볼 수 있습니다.

http://en.wikipedia.org/wiki/Mersenne_twister

속도와 무작위성이 잘 조화되어 있으며 GPL 구현이 있습니다.


이것이 내가 내 요구에 사용하게 된 것입니다.

int range_upper_bound = 12345;
int random_number =((double)rand()/(double)range_upper_bound);

double randDouble()
{
  double out;
  out = (double)rand()/(RAND_MAX + 1); //each iteration produces a number in [0, 1)
  out = (rand() + out)/RAND_MAX;
  out = (rand() + out)/RAND_MAX;
  out = (rand() + out)/RAND_MAX;
  out = (rand() + out)/RAND_MAX;
  out = (rand() + out)/RAND_MAX;

  return out;
}

만큼 빠르지는 double X=((double)rand()/(double)RAND_MAX);않지만 배포가 더 좋습니다. 이 알고리즘은 RAND_MAX에만 균등 한 간격으로 반환 값을 선택할 수 있습니다. 이것은 RANDMAX ^ 6을 제공하므로 그 분포는 double의 정밀도에 의해서만 제한됩니다.

long double을 원하면 몇 번의 반복을 추가하십시오. [0, 1]이 아닌 [0, 1]에있는 숫자를 원한다면 4 번째 줄을 읽기만하면 out = (double)rand()/(RAND_MAX);됩니다.


//Returns a random number in the range (0.0f, 1.0f).
// 0111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
// seee eeee eeee vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv
// sign     = 's'
// exponent = 'e'
// value    = 'v'
double DoubleRand() {
  typedef unsigned long long uint64;
  uint64 ret = 0;
  for (int i = 0; i < 13; i++) {
     ret |= ((uint64) (rand() % 16) << i * 4);
  }
  if (ret == 0) {
    return rand() % 2 ? 1.0f : 0.0f;
  }
  uint64 retb = ret;
  unsigned int exp = 0x3ff;
  retb = ret | ((uint64) exp << 52);
  double *tmp = (double*) &retb;
  double retval = *tmp;
  while (retval > 1.0f || retval < 0.0f) {
    retval = *(tmp = (double*) &(retb = ret | ((uint64) (exp--) << 52)));
  }
  if (rand() % 2) {
    retval -= 0.5f;
  }
  return retval;
}

이것은 트릭을 할 것입니다. 나는 이것을 만드는 것을 돕기 위해이 Wikipedia 기사를 사용했습니다. 나는 그것이 좋은 것이라고 믿습니다drand48();

참고 URL : https://stackoverflow.com/questions/1340729/how-do-you-generate-a-random-double-uniformly-distributed-between-0-and-1-from-c

반응형