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();
'ProgramingTip' 카테고리의 다른 글
내 구성 요소 바인딩이에서 정의되지 않은 이유는 무엇입니까? (0) | 2020.12.08 |
---|---|
Java 용 최고의 iCalendar 라이브러리? (0) | 2020.12.08 |
자바 펼쳐에서 '정의되지 않음'을 처리하는 방법 (0) | 2020.12.08 |
이 UI 패턴은 무엇입니까? (0) | 2020.12.08 |
그래프 위트 Matplotlib의 배경색 불투명도를 설정하는 방법 (0) | 2020.12.08 |