이것은 이전에 게시 된 질문의 후속입니다.
주사위의 측면을 모방하기 위해 1 ~ 6과 같은 특정 범위 내에서 임의의 숫자를 생성 할 수 있기를 바랍니다.
어떻게하면 되나요?
이것은 이전에 게시 된 질문의 후속입니다.
주사위의 측면을 모방하기 위해 1 ~ 6과 같은 특정 범위 내에서 임의의 숫자를 생성 할 수 있기를 바랍니다.
어떻게하면 되나요?
답변:
지금까지의 모든 답은 수학적으로 잘못되었습니다. 반환 하는 구간의 길이를 나누지 않는 한 (즉, 2의 거듭 제곱) 반환 rand() % N
은 범위 내의 숫자를 균일하게 제공하지 않습니다 . 또한의 모듈 리가 독립적 인지 여부를 알지 못합니다. 균일하지만 매우 무작위 적이 지 않은. 합리적으로 보이는 유일한 가정 은 포아송 분포를내는 것입니다. 같은 크기의 겹치지 않는 두 부분 구간은 똑같이 가능성이 있고 독립적입니다. 유한 한 값 집합의 경우 이는 균일 한 분포를 의미하며의 값 이 잘 분산되도록합니다.[0, N)
N
rand()
rand()
0, 1, 2, ...
rand()
rand()
즉, 범위를 변경하는 유일한 올바른 방법은 rand()
상자로 나누는 것입니다. 예를 들어 RAND_MAX == 11
범위를 원하는 경우 1, 2 등으로 1..6
할당해야합니다 . 이들은 분리되고 동일한 크기의 간격이므로 균일하고 독립적으로 분포됩니다.{0,1}
{2,3}
부동 소수점 나누기를 사용하라는 제안은 수학적으로 타당하지만 원칙적으로 반올림 문제가 있습니다. 아마도 double
그것을 작동시키기에 충분히 높은 정밀도 일 것입니다. 아마 아닐거야. 나는 모르고 그것을 알아 내고 싶지도 않다. 어쨌든 대답은 시스템에 따라 다릅니다.
올바른 방법은 정수 산술을 사용하는 것입니다. 즉, 다음과 같은 것을 원합니다.
#include <stdlib.h> // For random(), RAND_MAX
// Assumes 0 <= max <= RAND_MAX
// Returns in the closed interval [0, max]
long random_at_most(long max) {
unsigned long
// max <= RAND_MAX < ULONG_MAX, so this is okay.
num_bins = (unsigned long) max + 1,
num_rand = (unsigned long) RAND_MAX + 1,
bin_size = num_rand / num_bins,
defect = num_rand % num_bins;
long x;
do {
x = random();
}
// This is carefully written not to overflow
while (num_rand - defect <= (unsigned long)x);
// Truncated division is intentional
return x/bin_size;
}
루프는 완벽하게 균일 한 분포를 얻기 위해 필요합니다. 예를 들어, 0에서 2까지의 임의의 숫자가 주어지고 0에서 1까지의 숫자 만 원하면 2를 얻지 못할 때까지 계속 당기십시오. 이것이 동일한 확률로 0 또는 1을 제공하는지 확인하는 것은 어렵지 않습니다. 이 방법은 다르게 코딩되었지만 nos가 대답 한 링크에도 설명되어 있습니다. 나는 더 나은 배포판을 가지고 random()
있기보다는 rand()
(에 대한 man 페이지에서 언급했듯이 rand()
) 사용하고 있습니다.
기본 범위를 벗어난 임의의 값을 얻으려면 [0, RAND_MAX]
까다로운 작업을 수행해야합니다. 아마도 가장 편리한 함수를 정의하는 것입니다 random_extended()
끌어 n
비트 (사용 random_at_most()
으로) 되돌아 [0, 2**n)
한 다음 적용 random_at_most()
과 random_extended()
대신에 random()
(그리고 2**n - 1
대신에 RAND_MAX
) 이하의 임의의 값을 뽑아 2**n
당신이 그런를 보유 할 수있는 숫자 유형이 가정, 가치. 마지막으로, 물론 음수 값을 포함 [min, max]
하여를 사용하여 값을 얻을 수 있습니다 min + random_at_most(max - min)
.
max - min > RAND_MAX
. 이는 위에서 언급 한 문제보다 더 심각한 문제입니다 (예 : VC ++에는 RAND_MAX
32767 만 있음).
do {} while()
.
@Ryan Reich의 답변에 이어 정리 된 버전을 제공 할 것이라고 생각했습니다. 첫 번째 경계 검사는 두 번째 경계 검사를 고려할 때 필요하지 않으며 재귀 적이 아닌 반복적으로 만들었습니다. [최소, 최대] 범위의 값을 반환합니다 . 여기서 max >= min
및 1+max-min < RAND_MAX
.
unsigned int rand_interval(unsigned int min, unsigned int max)
{
int r;
const unsigned int range = 1 + max - min;
const unsigned int buckets = RAND_MAX / range;
const unsigned int limit = buckets * range;
/* Create equal size buckets all in a row, then fire randomly towards
* the buckets until you land in one of them. All buckets are equally
* likely. If you land off the end of the line of buckets, try again. */
do
{
r = rand();
} while (r >= limit);
return min + (r / buckets);
}
limit
int (및 선택적으로 bucket
도) 를 만들어 쉽게 해결됩니다 . 수정 : 제안서를 제출하고 수정했습니다. RAND_MAX / range
INT_MAX
buckets * range
RAND_MAX
다음은 범위의 최대 값과 최소값을 알고 있고 범위 사이에 포함 된 숫자를 생성하려는 경우 공식입니다.
r = (rand() % (max + 1 - min)) + min
int
으로 오버 플로우 max+1-min
.
unsigned int
randr(unsigned int min, unsigned int max)
{
double scaled = (double)rand()/RAND_MAX;
return (max - min +1)*scaled + min;
}
(((max-min+1)*rand())/RAND_MAX)+min
하고 아마도 정확히 동일한 분포를 얻을 수 있습니다 (RAND_MAX가 int에 비해 오버플로되지 않을 정도로 충분히 작다고 가정).
max + 1
경우 중 하나, rand() == RAND_MAX
또는 rand()
매우 가까이 RAND_MAX
와 부동 소수점 오류는 최종 결과 과거를 밀어 max + 1
. 안전을 위해 결과를 반환하기 전에 결과가 범위 내에 있는지 확인해야합니다.
RAND_MAX + 1.0
. 나는 확실하지의 좋은의 정도는 방지 할 수 있다는 여전히 해요 max + 1
특히 :하지만, 수익을 + min
마지막에 생산 끝낼 수 있었다 라운드 포함 max + 1
랜드의 큰 값 ()에 대한합니다. 이 접근 방식을 완전히 포기하고 정수 산술을 사용하는 것이 더 안전합니다.
RAND_MAX
가 대체 되면 정수 산술을 사용하여 수행 RAND_MAX+1.0
되는 경우 안전하다고 생각합니다 .. (분명하지 않은) 이유는 IEEE 754 산술과 반올림을 가정하고 (또한 정확히 double로 표현할 수 있지만 일반적인 시스템에서는 사실입니다) 항상 사실입니다 . 모든 양의 double 및 모든 double 만족 . + min
return (unsigned int)((max - min + 1) * scaled) + min
max - min + 1
x * scaled < x
x
scaled
0.0 <= scaled && scaled < 1.0
randr(0, UINT_MAX)
: 항상 0을 생성합니다.
그냥하지 않겠습니까?
srand(time(NULL));
int r = ( rand() % 6 ) + 1;
%
모듈러스 연산자입니다. 기본적으로 6으로 나누고 나머지를 반환합니다 ... from 0-5
rand()
생성기 상태의 하위 비트 (LCG를 사용하는 경우)를 포함하는 모든 곳에서 사용중인 libc를 보여줍니다 . 지금까지 하나도 보지 못했습니다. 모두 (예, RAND_MAX가 32767 인 MSVC를 포함하여) 하위 비트를 제거 합니다. 모듈러스를 사용하는 것은 다른 이유로 권장되지 않습니다. 즉, 더 작은 숫자를 선호하는 분포를 왜곡하기 때문입니다.
편향 문제를 이해하고 있지만 거부 기반 방법의 예측할 수없는 런타임을 견딜 수없는 사람들을 위해이 시리즈는 [0, n-1]
간격 에서 점차적으로 편향된 임의의 정수를 생성 합니다.
r = n / 2;
r = (rand() * n + r) / (RAND_MAX + 1);
r = (rand() * n + r) / (RAND_MAX + 1);
r = (rand() * n + r) / (RAND_MAX + 1);
...
고정 소수점 임의의 i * log_2(RAND_MAX + 1)
비트 수 ( i
반복 횟수) 를 합성하고에 의해 긴 곱셈을 수행하여이를 수행합니다 n
.
에 비해 비트 수가 충분히 크면 n
바이어스가 헤아릴 수 없을 정도로 작아집니다.
이 질문 에서와 같이 RAND_MAX + 1
이보다 작은 지 n
( 이 질문 에서와 같이 ) 또는 2의 거듭 제곱 이 아닌지 여부 는 중요하지 않지만 RAND_MAX * n
큰 경우 정수 오버플로를 방지하려면주의해야합니다 .
RAND_MAX
는 자주 INT_MAX
이므로 RAND_MAX + 1
-> UB (예 : INT_MIN)
RAND_MAX * n
가 크면 주의해야합니다"라는 의미 입니다. 요구 사항에 적합한 유형을 사용하도록 준비해야합니다.
RAND_MAX
는 종종 INT_MAX
"예,하지만 16 비트 시스템에서만 가능합니다! 합리적으로 현대적인 건축물은 INT_MAX
2 ^ 32 / 2와 RAND_MAX
2 ^ 16 / 2에 놓이게 될 것 입니다. 이것은 잘못된 가정입니까?
int
컴파일러를 테스트했으며 , RAND_MAX == 32767
하나와 RAND_MAX == 2147483647
다른 컴파일러를 찾았 습니다 . 내 전반적인 경험 (수십 년)은 RAND_MAX == INT_MAX
더 자주 있습니다. 따라서 합리적으로 현대적인 32 비트 아키텍처가 확실히 RAND_MAX
at 2^16 / 2
. C 스펙이 허용하기 때문에 32767 <= RAND_MAX <= INT_MAX
나는 경향이 아니라 어쨌든 그것을 코딩합니다.
모듈로 편향을 피하기 위해 (다른 답변에서 제 안됨) 항상 다음을 사용할 수 있습니다.
arc4random_uniform(MAX-MIN)+MIN
여기서 "MAX"는 상한이고 "MIN"은 하한입니다. 예를 들어 10에서 20 사이의 숫자의 경우 :
arc4random_uniform(20-10)+10
arc4random_uniform(10)+10
간단한 솔루션이며 "rand () % N"을 사용하는 것보다 낫습니다.
#include <bsd/stdlib.h>
먼저 해야 할 일이 있습니다. 또한 MinGW 또는 CygWin없이 Windows에서 이것을 얻는 방법에 대한 아이디어가 있습니까?
다음은 Ryan Reich의 솔루션보다 약간 더 간단한 알고리즘입니다.
/// Begin and end are *inclusive*; => [begin, end]
uint32_t getRandInterval(uint32_t begin, uint32_t end) {
uint32_t range = (end - begin) + 1;
uint32_t limit = ((uint64_t)RAND_MAX + 1) - (((uint64_t)RAND_MAX + 1) % range);
/* Imagine range-sized buckets all in a row, then fire randomly towards
* the buckets until you land in one of them. All buckets are equally
* likely. If you land off the end of the line of buckets, try again. */
uint32_t randVal = rand();
while (randVal >= limit) randVal = rand();
/// Return the position you hit in the bucket + begin as random number
return (randVal % range) + begin;
}
Example (RAND_MAX := 16, begin := 2, end := 7)
=> range := 6 (1 + end - begin)
=> limit := 12 (RAND_MAX + 1) - ((RAND_MAX + 1) % range)
The limit is always a multiple of the range,
so we can split it into range-sized buckets:
Possible-rand-output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Buckets: [0, 1, 2, 3, 4, 5][0, 1, 2, 3, 4, 5][X, X, X, X, X]
Buckets + begin: [2, 3, 4, 5, 6, 7][2, 3, 4, 5, 6, 7][X, X, X, X, X]
1st call to rand() => 13
→ 13 is not in the bucket-range anymore (>= limit), while-condition is true
→ retry...
2nd call to rand() => 7
→ 7 is in the bucket-range (< limit), while-condition is false
→ Get the corresponding bucket-value 1 (randVal % range) and add begin
=> 3
RAND_MAX + 1
int
추가 를 쉽게 넘칠 수 있습니다 . 이 경우 (RAND_MAX + 1) % range
의심스러운 결과가 생성됩니다. 고려(RAND_MAX + (uint32_t)1)
Ryan이 맞지만 무작위성의 원인에 대해 알려진 것을 기반으로 솔루션은 훨씬 더 간단 할 수 있습니다. 문제를 다시 설명하려면 :
[0, MAX)
균등 분포 로 범위 내에서 정수를 출력하는 임의성의 근원이 있습니다 .[rmin, rmax]
에서 균일하게 분포 된 임의의 정수를 생성하는 것입니다 0 <= rmin < rmax < MAX
.빈 (또는 "박스")의 수는 원래 숫자의 범위보다 훨씬 작은 경우 내 경험에, 그리고 원본 소스 강력한 암호입니다 - 모든 rigamarole을 통과 할 필요가없고, 간단한 모듈 부문 것 충분하고 (예 : output = rnd.next() % (rmax+1)
, if rmin == 0
), 속도 손실없이 "충분히"균일하게 분포 된 난수를 생성합니다. 핵심 요소는 임의성 소스입니다 (예 : 어린이, 집에서를 사용하지 마십시오 rand()
).
실제 작동 방식에 대한 예제 / 증명입니다. 나는 임의의 바이트 (Intel RDRAND 기반)를 생성하는 강력한 암호화 소스를 사용하여 1에서 22까지의 난수를 생성하고 싶었습니다. 결과는 다음과 같습니다.
Rnd distribution test (22 boxes, numbers of entries in each box): 1: 409443 4.55% 2: 408736 4.54% 3: 408557 4.54% 4: 409125 4.55% 5: 408812 4.54% 6: 409418 4.55% 7: 408365 4.54% 8: 407992 4.53% 9: 409262 4.55% 10: 408112 4.53% 11: 409995 4.56% 12: 409810 4.55% 13: 409638 4.55% 14: 408905 4.54% 15: 408484 4.54% 16: 408211 4.54% 17: 409773 4.55% 18: 409597 4.55% 19: 409727 4.55% 20: 409062 4.55% 21: 409634 4.55% 22: 409342 4.55% total: 100.00%
나는 공정한 주사위 같은 차 세계 대전 암호 기계 강력하게 암호화 코드북 생성 던져 (내 목적을 위해 필요로하는이 균일에 가까운입니다 http://users.telenet.be/d.rijmenants/en/kl-7sim.htm 등 ). 출력은 눈에 띄는 편향을 보이지 않습니다.
암호화가 강력한 (진정한) 난수 생성기의 소스는 인텔 디지털 난수 생성기 와 64 비트 (부호없는) 난수를 생성하는 샘플 코드입니다.
int rdrand64_step(unsigned long long int *therand)
{
unsigned long long int foo;
int cf_error_status;
asm("rdrand %%rax; \
mov $1,%%edx; \
cmovae %%rax,%%rdx; \
mov %%edx,%1; \
mov %%rax, %0;":"=r"(foo),"=r"(cf_error_status)::"%rax","%rdx");
*therand = foo;
return cf_error_status;
}
나는 Mac OS X에서 clang-6.0.1 (스트레이트)을 사용하고 gcc-4.8.3에서 "-Wa, q"플래그를 사용하여 컴파일했습니다 (GAS는 이러한 새로운 명령을 지원하지 않기 때문입니다).
gcc randu.c -o randu -Wa,q
(Ubuntu 16의 GCC 5.3.1) 또는 clang randu.c -o randu
(Clang 3.8.0)으로 컴파일 되지만 런타임에 Illegal instruction (core dumped)
. 어떤 아이디어?
앞서 말했듯이 모듈로는 분포를 왜곡하기 때문에 충분하지 않습니다. 다음은 비트를 마스킹하고 배포가 왜곡되지 않도록 사용하는 코드입니다.
static uint32_t randomInRange(uint32_t a,uint32_t b) {
uint32_t v;
uint32_t range;
uint32_t upper;
uint32_t lower;
uint32_t mask;
if(a == b) {
return a;
}
if(a > b) {
upper = a;
lower = b;
} else {
upper = b;
lower = a;
}
range = upper - lower;
mask = 0;
//XXX calculate range with log and mask? nah, too lazy :).
while(1) {
if(mask >= range) {
break;
}
mask = (mask << 1) | 1;
}
while(1) {
v = rand() & mask;
if(v <= range) {
return lower + v;
}
}
}
다음의 간단한 코드를 통해 배포판을 볼 수 있습니다.
int main() {
unsigned long long int i;
unsigned int n = 10;
unsigned int numbers[n];
for (i = 0; i < n; i++) {
numbers[i] = 0;
}
for (i = 0 ; i < 10000000 ; i++){
uint32_t rand = random_in_range(0,n - 1);
if(rand >= n){
printf("bug: rand out of range %u\n",(unsigned int)rand);
return 1;
}
numbers[rand] += 1;
}
for(i = 0; i < n; i++) {
printf("%u: %u\n",i,numbers[i]);
}
}
v = rand(); if (v > RAND_MAX - (RAND_MAX % range) -> reject and try again; else return v % range;
모듈로가 마스킹보다 훨씬 느린 작업이라는 것을 이해합니다.하지만 여전히 ..... 테스트해야한다고 생각합니다.
rand()
int
범위에서를 반환합니다 [0..RAND_MAX]
. 그 범위의 하위 범위를 쉽게 할 수있는 uint32_t
다음 randomInRange(0, ,b)
의 범위에서 값을 생성 없다 (INT_MAX...b]
.