범위 내에서 임의의 정수값을 생성하는 방법
이것은 이전에 투고된 질문의 후속 조치입니다.
다이의 측면을 모방하기 위해 1에서 6까지와 같은 특정 범위 내에서 난수를 생성할 수 있기를 바랍니다.
이걸 어떻게 하면 좋을까요?
지금까지의 답은 모두 수학적으로 틀렸다. ★★★★rand() % N
는, 「」의 하고 있지 않습니다.[0, N)
~가 아니면N
、 divides 、 divides 、 divides 、 divides divides into into into into into into into divides dividesrand()
) 반환(,, 2의 거듭제곱) (즉, 2의 거듭제곱)의알수.의 모듈리인지 아닌지를 모른다.rand()
독립적이다: 그들이 갈 가능성이 있다.0, 1, 2, ...
균일하지만 매우 랜덤하지는 않습니다.하다고 생각되는 은, 「이럴지도 모른다」는 것이다.rand()
포아송 분포를 제시합니다. 같은 크기의 겹치지 않는 두 개의 하위 구간이 동등하고 독립적입니다.값의 에서는, 이것은 , 「」의 값이 「」의 값이 합니다.rand()
잘 흩어졌네요.
은, 「」의 이, 「」의 범위를 변경하는 것을 의미합니다.rand()
박스로 입니다.★★★★★★★★★★★★★★★★★,RAND_MAX == 11
'어느 정도'의 거죠.1..6
, 을 할당해야 .{0,1}
1 {2,3}
2시 정각이 구간은 크기가 같고 분리된 구간이므로 균일하고 독립적으로 분포됩니다.
부동소수점 나눗셈을 사용하는 제안은 수학적으로 타당하지만 원칙적으로 반올림 문제에 시달린다. ★★★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()
좋은 있기 에('의에서 알 수 )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값을 알고 있으며 범위 사이에 숫자를 생성하는 경우의 수식은 다음과 같이 입력합니다.
r = (rand() % (max + 1 - min)) + min
@Ryan Reichi.두 번째 경계 검사에서는 첫 번째 경계 검사가 필요하지 않으며 재귀가 아닌 반복 검사로 만들었습니다.됩니다.여기서 [min, max]는 [min, max]입니다.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);
}
Ryan이 옳지만 랜덤성의 근원에 대해 알려진 바에 따라 솔루션이 훨씬 더 단순해질 수 있습니다.문제를 다시 기술하려면:
- , 범위내의 합니다.
[0, MAX)
균일한 분포로 - 정수 입니다.
[rmin, rmax]
서 ''는0 <= rmin < rmax < MAX
.
제 경험상, 만약 빈(또는 "박스")의 수가 원래 숫자의 범위보다 훨씬 적고, 원래의 소스가 암호학적으로 강력하다면, 모든 rigamarole을 통과할 필요가 없으며, 단순한 모듈로 분할로도 충분합니다(예:output = rnd.next() % (rmax+1)
if, if, if, if.rmin == 0
속도 저하 없이 균등하게 분포된 난수를 생성합니다.).rand()
를 참조해 주세요.
다음은 실제 작동 방식에 대한 예/증명입니다.1부터 22까지의 랜덤 번호를 생성하려고 했습니다.암호화할 수 있는 강력한 소스가 랜덤 바이트(인텔 RDRAND 기반)를 생성하려고 했습니다.결과는 다음과 같습니다.
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, 등의 제2차 세계대전 암호 기계용 암호책 생성 등).출력에 현저한 치우침은 없습니다.
다음은 암호학적으로 강력한(진정한) 난수 생성기의 소스입니다.인텔 디지털 난수 생성기 및 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는 이러한 새로운 명령을 지원하지 않기 때문입니다).
다음은 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
하지만 거부 방식의 예측 수 없는 이 으로 덜 합니다.[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);
...
it it 、 it 、 것 、 it 、 it 、 it 、 it 、 it 、 。i * log_2(RAND_MAX + 1)
여기서 "bits"(")i
긴 을 합니다.n
.
가 트음음음음에 큰 n
을 사용하다
RAND_MAX + 1
n
(이 질문에서와 같이) 또는 2의 거듭제곱이 아닌 경우에는 정수 오버플로를 방지하기 위해 주의해야 합니다.RAND_MAX * n
★★★★★★ 。
그냥 하지 않을래?
srand(time(NULL));
int r = ( rand() % 6 ) + 1;
%
을 사용하다에서 50까지
unsigned int
randr(unsigned int min, unsigned int max)
{
double scaled = (double)rand()/RAND_MAX;
return (max - min +1)*scaled + min;
}
그 외의 옵션에 대해서는, 여기를 참조해 주세요.
모듈로 편향(다른 답변에서 제안됨)을 피하기 위해 항상 다음을 사용할 수 있습니다.
arc4random_uniform(MAX-MIN)+MIN
여기서 "MAX"는 상한이고 "MIN"은 하한입니다.예를 들어, 10 ~20 의 숫자의 경우:
arc4random_uniform(20-10)+10
arc4random_uniform(10)+10
간단한 해결책으로 "rand() % N"을 사용하는 것보다 낫다.
modulo는 이전에도 말했듯이 분포가 왜곡되어 있기 때문에 충분하지 않습니다.비트를 숨기고 분포가 왜곡되지 않도록 사용하는 내 코드입니다.
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]);
}
}
[0,1] 범위의 부동 소수점 숫자를 반환합니다.
#define rand01() (((double)random())/((double)(RAND_MAX)))
언급URL : https://stackoverflow.com/questions/2509679/how-to-generate-a-random-integer-number-from-within-a-range
'programing' 카테고리의 다른 글
[Vue warn] :알 수 없는 사용자 지정 요소: (0) | 2022.08.16 |
---|---|
물리 디스크를 표시하는 방법 (0) | 2022.08.16 |
포인터를 전달하는 것이 아니라 C의 값으로 구조물을 통과하는 단점이 있습니까? (0) | 2022.08.16 |
할당된 메모리 청크를 읽기 전용으로 표시하는 방법이 있습니까? (0) | 2022.08.16 |
Vue 템플릿에서 'this'를 사용하시겠습니까? (0) | 2022.08.16 |