gcc와 VC 컴파일러에서 같이 사용 가능한 INT_MAX 범위의 rand 함수를 구현하는 방법 있나요?

kafob의 이미지

실제 linux에서는 rand()함수의 범위가 INT_MAX의 범위(2^32인가?)로 되어있고

VC 컴파일러(제가 쓰는 것은 VS 2008)에서는 rand() 함수 범위가 SHRT_MAX의 범위(2^15-1)로 되어 있는데

이것 때문에 지금 linux에서 코딩한 C 함수를 dll로 바꾸려고 VC를 사용하는데 rand()함수 범위가 달라서

서로 공통적으로 사용할 수 있는 함수를 찾을려고 하니까 잘 못찾겠네요... ;;

그래서 코드 내용은 동일하되 공통적으로 사용할 수 있는 rand()를 사용할 수 있는 방법 없나요??

답변 부탁드립니다.

drinkme의 이미지

정확히 말하면,
rand()함수는 0~RAND_MAX 의 난수를 보내줍니다.
RAND_MAX는 system/compiler마다 다를 수 있겠죠.

만약, 0~99 의 값을 원하신다면,
보통은
#define RAND(n) ((double)rand()*((double)(n)+(double)1)/((double)(RAND_MAX)+(double)1))
원하시는값=RAND(99);

이렇게 쓰거든요.

klyx의 이미지

보통이라면, rand()%100 과 같이 하지 않나요?
어느걸 보통이라고 하느냐 자체는 '보통'이란 단어의 정의에 대한 문제이기도 합니다만..
RAND_MAX가 플랫폼에 따라 다를지라도 수백 정도의 차이는 무시할수 있을 정도의 크기는 되니까요.
SHRT_MAX와 같은 값인 경우라면 수천단위는 무시하기 어렵겠지만..

drinkme의 이미지

http://members.cox.net/srice1/random/crandom.html
중간쯤의 단락을 읽어보세요.

nhamfnad의 이미지

위의 글이 10년전에 써졌고.. 예제 코드를 보니. seed값이 고정된 상태에서
랜덤함수를 부르면 비슷한 값이 순환된다는 것인데
그 문제는 이미 seed 값을 변화시키면 된다고 이미 오래전에 답 나온것입니다.
결국 rand % n 을 써도 seed 값을 변화시키면 원하는 랜덤값을 얻을수 있습니다.

올바른 방법입니다.

drinkme의 이미지

일반적으로 random함수는요.
난수영역대에서 고른 분포를 가져야, 좋은 난수함수가 되는 건데요.

random함수를 호출할때마다 seed를 임의로 변화시키는 방법은
이러한 면에서 그다지 추천할 만한 방법이 아니라고 생각합니다.

hjlee의 이미지

rand % n 이 좋지 않은 또 다른 이유는
MAX_RAND 가 n 으로 나누어 떨어지지 않을 경우, 고른 분포의 rand 값을 가지지 않게 되기 때문입니다.
(보통 n 이 2^k 이 아닌 경우이겠지요.)

예를 들어 0~99 까지의 고르게 난수를 발생시키는 난수 발생기를 rand100() 이라고 불러봅시다.

rand100() % 70 은 0~69 까지 고른 분포를 보일까요?
0~29 까지의 분포가 더 많게 됩니다.

rand100() % 15 는 0~14 까지 고른 분포를 보일까요?
0~9 까지의 분포가 더 많게 됩니다.

http://java.sun.com/javase/6/docs/api/java/util/Random.html#nextInt(int)
에서 이 문제를 해결한 구현과 설명을 보실 수 있습니다.
(c 로 된 예제나 자세한 설명도 어디 있겠지만, 제가 얼마 전에 다른 이유로 찾아본 내용이라 이 링크를 올립니다.)

---
추가 -
제가 drinkme 님과 거의 같은 얘기를 한 것이군요.
(수정 - rand100 "고르게 난수를 발생시키는" )
추가2 -
링크의 내용은 고른 분포 이야기가 아니라 low-order bit 이야기이네요. ㅡ.ㅡ

eungkyu의 이미지

제가 주신 자바 링크를 봤는데 실력이 딸리는지 잘 이해가 안가서 질문 올립니다.

보니 자바의 nextInt(n)은 0 ~ n-1의 random 값을 잘 뽑기 위해
기존의 next(31) % n 에 추가로 두 가지 방법을 이용합니다.

1. n이 2^k인 경우 higher bit를 리턴해주기
2.

   int bits, val;
   do {
       bits = next(31);
       val = bits % n;
   } while (bits - val + (n-1) < 0);

위와 같은 코드를 이용하여 조건에 맞지 않는 경우 다시 뽑기

그러면서 아래에 얘기하기를 1번의 경우 "high-order bit를 뽑기 위함이다. 왜냐 하면 이 클래스에서 구현한 random number generator는 low-order bit의 randomness가 좋지 않기 때문이다." 라고 하였습니다.

그럼 결국 low-order bit 얘기 외에 남은 것은 2번 스페셜 케이스에 대한 사항인데 제가 이해하기가 힘드네요. 어떤 이유로 해서 bits - val + (n-1) < 0인 경우를 reject하는지...

설명 부탁드려도 될까요?

hjlee의 이미지

우선 알고리즘의 핵심은 고른 분포가 되도록 하기 위해서 고른 분포가 되지 않게 하는 결과들은 버리겠다는 것입니다.

rand100() 에서 rand70() 을 얻고자 한다면
rand100() 의 결과가 70~99 인 경우를 버리고,
rand100() 에서 rand15() 를 얻고자 한다면 rand100() 의 결과가 90~99 인 경우를 버리는 것이죠.

while 문 안의 조건은 이것을 overflow 를 이용해서 check 하는 것입니다.
bits 와 val 가 long 이라면, 조건은 bits-val + (n-1) >= 2^32 과 같은 의미입니다.
=> bits-val+(n-1) >= RANDMAX+1
=> bits-val+n > RANDMAX+1

예를 rand100() 과 rand15() 의 예로 변환해보면

do {
  bits = rand100()
  val = bits % 15
} while (bits - val + 15 > 100)

이렇게 됩니다. 90~99 인 경우는 버리는 것이죠.
jick의 이미지

random이 해주는 일이 "일단 하나의 seed를 주면 그때부터 '거의 랜덤처럼 보이는' 수열을 뽑아주기"인데, 중간 중간에 seed를 바꾸면 이 가정이 무너집니다.

seed를 같은 값을 준 다음 rand를 부르면 같은 값이 돌아오겠죠. 마찬가지로 seed를 A라는 알고리즘에 따라 바꾸면 A만큼 랜덤한 값이 돌아오겠죠.

결국 이런 식으로 할 때 rand에서 돌려주는 값은 "rand를 구현한 알고리즘에서 보장하는 만큼 랜덤한 값"이 아니라 "사용자가 seed를 바꾸는 알고리즘에서 보장하는 만큼 랜덤한 값"이 됩니다. 난수 구현은 까다롭고 함정이 많은 문제기 때문에, 사용자의 알고리즘이 rand보다 더 좋을 거라는 보장이 없습니다.

* 제가 잘 아는 분야가 아니라서 좀 부정확하게 썼을 수 있습니다. 양해 부탁드립니다.

nhamfnad의 이미지

...

grassman의 이미지

중간 부분에서 rand() 함수의 최하위 비트가 0, 1을 반복한다고 되어 있습니다만...
이 부분은 실제로 GNU libc의 구현을 보면 그렇게 구현되어 있는 부분이 있으나 실제로 사용하지 않는 것을 간단한 실험으로 파악할 수 있습니다. 그리고 rand() 함수의 하위 비트가 덜 random 하다는 얘기는 이미 오래 전에 논의가 되었던 것으로 알고 있습니다. 새로운 구현을 사용하고 있다면 이 부분에 대해서도 보강이 되어 있지 않을까요?

아래는 glibc 2.9의 random_r.c 파일 내용 중에서 기존 random 구현에 대한 부분입니다. 이대로라면 state 값은 짝수와 홀수 상태를 반복하게 됩니다.

val = ((state[0] * 1103515245) + 12345) & 0x7fffffff;
state[0] = val;

그러나 실제로 Visual C++ 6.0과 MinGW gcc 4.3.0을 사용하여 실험해보면 rand() 함수가 처음부터 짝수와 홀수를 교대로 생성하지 않음을 알 수 있습니다.

즉, 문서의 내용은 현재 사용 중인 구현체보다 오래된 내용이며 현실적으로 rand() % MAX 형태로 사용해도 문제가 되지 않을 겁니다.

drinkme의 이미지

아... 이거 맞는 말씀을 괜히 딴지걸로 있는거 같아서
마음이 편하지 않지만요... 한마디만 더 할께요. 죄송합니다.

제가 % 방법에 대에 좋지 않다고 말씀드린 부분은요.

만약,
rnd() % MAX 로 난수를 생성했을 경우,
0~(MAX-1)구간의 숫자가 나올 확률이
MAX~RAND_MAX 구간에 비해, 1/(INT(RAND_MAX/MAX)-1) 배 높다는 거죠. (맞나?)

그리고, rnd()함수 자체가
0~RAND_MAX 사이에서 가급적 고른 분포도를 목적으로 설계되기 때문에
rnd()%MAX 로 난수를 생성할 경우,
0~(MAX-1) 구간에서 얼마나 고른 분포도를 가진 난수를 생성할 수 있느냐는
보장할 수 없다는 거죠.

그리고요, 제가 link한 문서가 옛날 문서라고 말씀하시는데,
오래되었고 아니고가 얼마나 중요한지는 제가 잘 모르겠거든요.
(가령, 제가 random 함수에 대해 학교에서 강의 들은지 15년정도 되는 것 같습니다.
그 사이에 random함수의 필요조건이 바뀌었다면 이 문서는 pass입니다.)
random 함수의 알고리즘이나, 구현방법은요, 아주 많고요, 또한 새로 개발되고 있습니다.
또, 최근에는 hardware random generator를 사용하는 경우도 있고요.
(물론, C library가 그걸 사용하는지는 모르겠고요)
좋은 random 함수의 요건은 몇가지가 있겠지만,
얼마나 예측하기 힘든(?) random한 값을 생성해 내느냐도 척도겠지만,
얼마나 고른 분포도를 가진 난수를 생성하느냐도 중요하거든요.

eungkyu의 이미지

인용한 문서를 살펴보겠습니다.

제가 보기에 핵심은 이 부분입니다.

Quote:
Note: Do NOT use

y = rand() % M;

as this focuses on the lower bits of rand(). For linear congruential random number generators, which rand() often is, the lower bytes are much less random than the higher bytes.

해석하자면, rand() 함수 내부적으로 쓰이는 알고리즘에 따라서 (linear congruential random number 알고리즘일 경우) lower bytes가 higher bytes보다 덜 random할 수 있기 때문에 저런 식으로 쓰지 말라는 것입니다. y = rand() % M 방식으로 뽑을 경우 상대적으로 randomness가 떨어지는 lower bytes만 활용할 가능성이 크기 때문입니다.

그럼 리눅스의 rand() 함수의 manpage를 인용하자면

Quote:
The versions of rand() and srand() in the Linux C Library use the same
random number generator as random(3) and srandom(3), so the lower-order
bits should be as random as the higher-order bits.

리눅스의 random()함수의 manpage도 살펴봅시다.

Quote:
The random() function uses a non-linear additive feedback random number
generator employing a default table of size 31 long integers to return
successive pseudo-random numbers in the range from 0 to RAND_MAX.

rand()는 random(3)을 사용하여 만들어졌으며, lower-order bits과 higher-order bits의 랜덤 정도가 동등한 알고리즘을 사용하고 있습니다. 즉, 적어도 현대의 리눅스에서는 y = rand() % M 라고 하는 것에 아무 문제가 없음을 알 수 있습니다.

다만, 모든 시스템의 랜덤 알고리즘이 다 그렇다고 할 수는 없으니 y = rand() % M 형식으로 추출하는 것을 무조건 옳다고 할 수는 없겠으나, 무조건 나쁜 방법라고 매도하는 것은 분명 잘못된 것입니다.

적어도 이 사이트에서의 주로 다루는 OS인 리눅스에서는 아무 문제가 없는 표현입니다.

Hyun의 이미지

drinkme님께서 말씀하시는건, RAND_MAX가 200이고, rand()%150 으로 0부터 149까지의 난수를 구하면, 0부터 50까지의 분포확률과 50부터 149까지의 분포확률이 다르다는 얘기인 것 같습니다. rand함수가 0부터 200까지의 영역에서 고른 분포로 난수를 생성하니깐 이것을 150에서 잘라서 윗부분을 0부터 50까지 중첩시켰으니깐 분포확률은 달라질 수 밖에 없습니다.

하지만, RAND_MAX가 충분히 큰 값이고(보통 얼마의 값인지는 모르겠습니다) 원하는 난수범위가 그에 비해 충분히 작다면 무시할 수 있는 확률분포를 가질 수 있겠지만 시물레이션 등 고른 분포를 얻을 땐 drinkme님께서 제시해주신 방법을 사용하는게 (느리지만) 좋을 듯 합니다.


나도 세벌식을 씁니다
drinkme의 이미지

아고. 자꾸 딴지 죄송합니다.

자꾸 얘기가 왜 GNU lib의 random 구현으로 쏠리는지 잘 모르겠고요.
(제가 이상한 건가요?)
제가 이해하기로는 최초의 이 문제는
platform간에 RND_MAX 범위에 무관한 난수생성입니다.

왜, glibc source를 근거로 내세우시는 분위 계신지도 잘 모르겠고,
rand() manpage를 말씀하시는지 모르겠고요.
(물론, 그걸 무시하자는게 아닙니다)

그리고, 저 위에위에 분이
M을 RND_MAX 범위의 하위 order 범위라고 말씀하신 것도 아닌데,
(저 위에는 분명 100이라고 적혀 있습니다)
그렇게 단정짓고 말씀하시는지도 잘 모르겠습니다.

저는 무언가를 매도하자는 것이 절대로 아닙니다.

제가 말씀드리는 '일반적인 난수생성에 대한 요건'이 이상한 건가요?
만약, 로또당첨 system의 source에
(rnd()%45)+1 로 되어 있다면,
가급적 RND_MAX%45+1 이내에 숫자로 선택할 것입니다.
이게 님이 말씀하시는 '아무 문제 없는 표현'인가요?

grassman의 이미지

점점 flame war 형태로 발전하는 양상이므로 조심스럽습니다만 일단 해명은 하고 넘어가야 할 듯 하군요.

rand() % MAX 형태가 rand() 함수의 난수 발생 확률 분포와 다르게 바뀔 수 있다는 점은 동의합니다.

그러나 링크로 걸어주신 글에는

1. RAND_MAX가 크지 않을 경우 결과값이 순환되는 부분이 발생하므로 RAND_MAX보다 훨씬 작은 수의 rand() 함수 호출만 쓸 것을 권하고 있으며
2. rand() % MAX 형태의 단점으로는 하위 비트의 randomness가 떨어진다는 점을 강조하고 있습니다.

그래서 제가 그에 대한 반론으로 답변을 단 것입니다. (특정 구현에 대해 인용한 것은 죄송합니다. 다만 현재 가장 널리 사용된다고 생각되는 구현이 저 글과 다름을 입증해 보이기 위해 사용한 겁니다.) modulo 연산을 사용한 결과의 확률 분포가 달라지는 것과 rand() 함수 결과값에서 하위 비트의 randomness가 떨어지는 것은 다른 얘기입니다. rand() 함수의 결과값에서 하위 비트의 randomness가 떨어지는 것은 특정 구현에 종속적인 얘기가 되는 것입니다.

아뭏튼 지금 주장하신 내용은 링크의 글에 나타나 있지 않았습니다.
논지가 그런 의도였다면 적절한 링크를 달아주셨으면 좋았을 거라고 생각합니다.

P.S. 기술은 계속 발전합니다. 따라서 기존 기술에 의존적인 내용이 있다면 그 기술이 보완된 이후에는 글을 갱신하는 것도 필요하다고 봅니다.

drinkme의 이미지

님의 말씀이 맞습니다.

저는 % 연산을 이용한 방법이 옳은지에 대해서 얘기를 하고 싶었고,
가령 이런 경우도 있기 때문이라는 말을 하고 싶었는데,
link 내용으로 인한 오해의 소지가 있었던 듯 하군요.

nhamfnad의 이미지

과거에 랜덤함수의 구현이 잘못되었기 때문에 잘못된 결과를 낼수 있다고 하는거죠.
C Faq 책에 보면 잘 나와 있습니다.
그런데 요즘은 랜덤함수의 구현이 과거에 비해 잘 구현이 되어있기 때문에 모듈로 연산을 해도
큰 무리가 없다고 생각됩니다. 일반인이 코딩을 줄이고 어느정도 신뢰성이 있는 랜덤수를 얻기 위해 rand() % n 을 써도 무방하다고 생각됩니다. (물론 수치계산이나 통계쪽은 엄격한 난수를 요구합니다만, 그 경우 rand()((double)n + (double)1)/((double)RAND_MAX + (double)1 를 써야 되지 않을까요.)
seed 값 변화를 주는것은 잘못 구현된 랜덤함수의 경우 머신간에 동일한 랜덤수열을 발생시키므로 seed 값을 변화시켜 중복을 피하게하는 오래된 묘책입니다만 (언급된 웹글이 써졌을 당시에) 요즘 gcc 맨페이지를 보면 동일한 seed 값을 주어도 다른 머신에서는 다른 수열이 나온다고 써있습니다.
즉 언급된 오래된 웹문서와는 다른 내용을 이야기 하고 있죠.

글에 다분히 오해의 소지가 있는것 같네요. (제가 좀 오해를 자주해서..OTL)

dorado2의 이미지


위에 나온 얘기를 반복하는 것 같아 죄송합니다만,

나온 글 내용들을 보면 랜덤함수의 구현이 올바르게 되었다고 하더라도,
모듈로 연산을 통해 고른 분포가 깨질 수 있기 때문에 rand()%n 은 여전히 좋지 않은 방법이 아닐까요.

JuEUS-U의 이미지

rand함수 쓴지 3,4년은 된듯하고 (시스템 프로그래밍 지못미)
rand() % n은 오늘 처음 안 방식인데도,
댓글 읽고 기분이 살짝 상했습니다 = _=)...
자료가 문제가 아니라 댓글 제목에 문제가 있지 싶습니다.

hjlee의 이미지

간과한 부분이 있는데(저를 포함), 이 방법도 고른분포 문제의 해결 방법은 아니라는 것입니다.
% 사용과 다른 점은 % 사용시 전반부 숫자의 발생 확률이 높아지고 나누는 방법은 중간 중간에 그런 숫자가 포함 된다는 차이가 있을뿐입니다.
이것은 두 방법 모두 (RAND_MAX+1) 의 숫자를 n 개의 숫자로 맵핑하는 방법이기 때문에 그렇습니다.
예를 들어 0~99 까지의 수를 만드는 rand100() 으로 0~69 까지의 수를 만들 경우 30개의 수는 다른 40개의 수보다 한 번 더 맵핑 될 수 밖에 없습니다.

좀 더 간단한 예로 0~9 까지 고른 분포를 보이는 rand10() 을 가정해 보겠습니다.

0~6 까지의 분포를 얻고 싶다면
((double)rand10()*((double)(6)+(double)1)/((double)(9)+(double)1))
-> (rand10()*((double)7))/((double)10)
0.0 0.7 1.4 2.1 2.8 3.5 4.2 4.9 5.6 6.3
0, 2, 4 가 나타날 확률이 1,3,5,6 이 나타날 확률보다 더 크게 됩니다.

eungkyu의 이미지

8비트짜리 랜덤 넘버를 발생시키는 함수를 rand8이라 하면
32비트짜리 랜덤 넘버를 발생시키는 rand32는 다음과 같이 구현할 수 있습니다.

rand32() = rand8() << 24 | rand8() << 16 | rand8() << 8 | rand8()

시지프스의 이미지

이거 안 될 것 같습니다. 만약 rand() 함수가 이상적이라면 상관 없습니다. 그러나 이 함수가 현재 값에서 다음 값을 계산하는 방식으로 구현되었기 때문에 문제가 됩니다. 함수의 상태의 수가 유한하므로 언젠가는 처음 상태로 돌아올 것이고, 현재 상태에서 다음 상태로 변하는 알고리즘이 정해져 있으므로 함수의 결과가 순환하게 됩니다. 예를 들어 rand_3()의 결과가 다음과 같은 수열을 이룬다고 가정해보겠습니다.
1, 7, 3, 5, 2, 0, 6, 4.
이 경우 rand_3()<<3 | rand_3()을 하면 0b001111은 나와도 0b001000은 안 나오게 됩니다. 그러므로 f() := rand_3()<<3 | rand_3()는 고른 분포를 가지지 않습니다.

begin{signature}
THIS IS SPARTA!!!!!n.
end{signature}

eungkyu의 이미지

맞네요. 대충 random을 구할땐 쓸 수 있겠지만 엄밀한 의미의 random값으론 쓰기 어려울 것 같습니다.

eungkyu의 이미지

다시 한번 생각해봤는데 (전공이 그쪽이 아니니까 정확하진 않을테지만)

값이 몇 개 빠진다고 해서 랜덤 분포가 아닌 것은 아니지 않나 합니다.
rand3 두개로 rand6을 만드는 케이스는 워낙 개수가 작으니까 문제가 훨씬 확대되어 느껴지는 것일 것 같구요

RAND_MAX가 15 bit인 것을 31bit를 만들기 위해 합치는 정도라면 분포에 큰 문제가 생기지는 않을 듯 합니다.
수학적으로 분포가 정말 중요한 곳에서라면 새로 만들어 써야겠지만,
새로운 랜덤 알고리즘을 짜서 만드는게 목적이 아니고
기존의 random 함수를 이용해서 적절히 max를 늘리는 목적이라면 충분히 달성하는게 아닌가 합니다.

추가: 공부하는 김에 확실히 해야겠다 하고 살펴 보니까 rand3이라고 해서 꼭 cycle이 2^3인 것은 아니네요.
내부 state를 더 크게 해서 cycle을 늘리는 듯 합니다. 따라서 그러한 경우는 더욱 더
rand3() << 3 + rand3() 이 문제가 없는 코드가 됩니다.

아르아의 이미지

rand % n에 대한 논의를 도대체 왜 해야 되는것인지 이해할 수 없네요.
drand * n;
(drand = (0, 1)사이의 real random number를 생성) 같은 한줄이면 아무 문제 없을것을
뭐하러 고생해서 "rand % n"를 씁니까. 그야말로 삽질이죠.
모르는 사람이야 뭐 모르니까 그럴 수 있다손 치더라도 말입니다.
rand % n 라고 써서 얻을 수 있는 이득이 있긴 할까요?
rand % n는 drand * n;보다 장점은 전혀 없고 단점만 많아 보입니다.
속도가 걱정이면 속도가 빠른 난수생성 알고리즘이나 라이브러리를 써야 할테고요.

얼마나 '진짜'에 가까운 난수가 필요한지는 문제마다 다릅니다.
'진짜 난수'를 쓰기 전에는 이 문제는 항상 고민해야 하고요.
"rand % n"는 예전에는 문제가 있었고, 지금도 문제가 있을 수'도' 있는 방식입니다.
"rand % n"를 사용하려면 그것이 해당 프로그램에는 문제를 안준다는것을 직접 검증해야 합니다.
차후 프로그램이 진화하게 되더라도 문제가 안되는것도 검증해야 하고요.

저~ 위에 뎃글중 '리눅스의 rand() 함수의 manpage'에 대한 인용한 부분이 보이는데,
"so the lower-order bits should be as random as the higher-order bits." 라고 되어 있네요.
왜 'should be'라고 했겠습니까? 장담을 못하니까죠. 문제마다 다르거든요.
아니면 단순명료하게 'are'라고 했겠죠.
괜히 장담했다가 잘못 될 수도 있는 위험을 왜 감당해야 합니까.

hjlee의 이미지

drand * n 도 아무 문제가 없는 것은 아닙니다.
rand % n 은 lower-bit 문제(없을 수도 있는) 뿐만 아니라, 고른 분포(uniform distribution)의 문제가 있습니다.
-- 고른 분포 문제 - (RANDMAX+1)%n 개의 숫자는 다른 숫자에 비해서 나타날 확률이 커지게 됩니다.
(double)((RANDMAX+1)/n)/(double)(RANDMAX+1) vs
(double)((RANDMAX+1)/n + 1)/(double)(RANDMAX+1)
(n 이 작으면 체감하기 어려울 수 있겠습니다만.)

문제는 drand 가 고른 분포 문제를 해결해 주지 *못한다는* 것입니다.
특히나 drand 를 ((double) rand()) / ((double)(RANDMAX+1)) 과 같이 구현했다면 전혀 다를 바가 없어집니다.

괜한 변환과 double 연산들만 하게 되는 모양이 됩니다.

물론 drand 를 좀 더 다양한 값을 가지도록 잘 구현할 수도 있겠지만, 여전히 만들어낼 수 있는 실수(floating point number)의 수는 제한이 있을 수 밖에 없습니다.
더군다나 drand 를 좀 더 다양한 값을 가지면서 고른 분포를 보이도록 구현하는 것은 생각만큼 쉬운 일이 아닙니다.
이 경우 고른 분포 문제에서 RANDMAX 가 커진 것과 같은 효과를 볼 수는 있는데, 그럴바에는 차라리 RANDMAX 가 큰 INT64 randl() 같은 것을 만드는 편이 RANDMAX 를 더 크게 할 수 있습니다.

결론적으로 drand 가 해결해 줄 수 있는 것은 lower-bit 문제에 국한됩니다.
drand 는 말 그대로 실수 난수가 필요한 때 쓰는 것이 바람직하고,
0~(n-1) 난수 생성은 엄격한 결과를 요하지 않고 n이 크지 않다면 rand()%n 을 쓰던가,
n 이 크거나 엄격한 난수 생성이 필요하다면 고른분포를 가지도록 잘 구현을 하는 것이 바람직할 것입니다.
(이런 경우라면 문제에 따라서 rand() 대신 H/W 난수 발생기나 따로 검증된 난수 생성 알고리즘을 구현해서 사용하는 것을 고려해야 할수도 있겠구요.)

java의 nextInt(int) 예 - http://java.sun.com/javase/6/docs/api/java/util/Random.html#nextInt(int)

--
수정 - 확률

아르아의 이미지

프로그램에 오류가 없다는것을 증명할 수도 없는데
제가 위의 덧글에 '아무 문제없다'고 한것은 분명 과장해서 말한것이긴 합니다.
말씀하신 문제가 있을 수 있죠.
그런데 제 뎃글의 결론이 'drand아무 문제없다'쪽으로 보이나요?
하늘이 두쪽나도 아무 문제없다는것이 아니라 왠만한경우에는 별 문제 없다고
받아들이는것이 보통이 아닐까 하는데요.
컴퓨터에 용량이란 항상 유한한 법인데 무한한 범위(자연수,실수)를
아무 문제 없이 다루는것은 거의 불가능한 일이고,
항상 정도의 문제가 따르게 됩니다.
제 요지는 'drand가 모든 면에서 rand % n보다는 좋다'는 것이고요.

물론 n이 아주 크면, 즉 문제에서 요구하는 정밀도가 n/randmax 보다도 작아야 하면
적절히 대처해줘야 하는것은 당연한 것이고요. 다 정도의 문제이죠.
물론 이런 의미에서 n이 커지거나 요구되는 엄밀성이 커질수록
uniform distribution문제를 더 신경써야 한다는것은 아주 중요한 지적입니다.

그런데 결론이 좀 이해가 안가는군요.

Quote:

결론적으로 drand 가 해결해 줄 수 있는 것은 lower-bit 문제에 국한됩니다.
drand 는 말 그대로 실수 난수가 필요한 때 쓰는 것이 바람직하고,
0~(n-1) 난수 생성은 엄격한 결과를 요하지 않고 n이 크지 않다면 rand()%n 을 쓰던가,
n 이 크거나 엄격한 난수 생성이 필요하다면 고른분포를 가지도록 잘 구현을 하는 것이 바람직할 것입니다.
(이런 경우라면 문제에 따라서 rand() 대신 H/W 난수 발생기나 따로 검증된 난수 생성 알고리즘을 구현해서 사용하는 것을 고려해야 할수도 있겠구요.)

drand가 lower-bit문제를 없애준다고 하셨으면서
"n이 크지 않다면 rand()%n 을 쓰던가"라는 결론이 나오나요? drand를 써야죠.
rand는 시스템에 따라서 아주 후질수도 있고 randmax가 2^16일수도 있으며, 반대로 아주 좋을 수도 있습니다.
그런데 시스템에 drand라는 이름이 붙은 함수가 존재한다면 그것은
randmax가 적어도 2^48이상이라는것을 의미합니다. double형이니까요.
현실적으로 충분히 큰 숫자죠.

dorado2의 이미지

drand가 drand48을 말씀하시는 건가요?

적어도 제 시스템 manpage에서는 obsolete된 함수여서 rand를 대신 사용하라고 되어있네요.

아르아의 이미지

특정 함수를 지칭한것이 아니라
(제대로된)double type random number를 생성할 수 있는 아무 함수를 말한겁니다.
RAND_MAX가 충분히 크다면 사실상 (rand() / (RAND_MAX + 1.0))와 동일하겠죠.
하지만 많은 경우 RAND_MAX는 2^32이하인듯 하더군요.

kafob의 이미지

저는 플랫폼에 관계 없이 RAND MAX 가 2^32 범위를 가지는 rand 함수를 물어봤는데 ;;;;

rand 자체에 대해서 토론 중이시네요 ^^

그러나 저는 아직 제가 원하는 구현을 못했다는거 ... ㅠㅠ

grassman의 이미지

RAND_MAX의 크기가 INT_MAX 보다 작다고 가정할 경우에는 새로 만들어 써야 합니다.
물론 새로 만든 방법이 완전한 randomness를 보장하는 것은 구현자의 책임입니다.

아래와 같은 코드를 작성하면 unsigned int 크기의 난수값을 발생시킵니다.

(또 다른 randomness에 대한 논의가 일어나는 것을 방지하기 위해 부연 설명을 붙입니다.
아래의 구현은 완전한 randomness를 보장하지 않습니다. 암호학이나 기타 정교한 random
함수를 필요로 하는 응용에 사용하는 것은 부적합할 수 있습니다.)

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <time.h>
 
#define MY_RAND_MAX     (rand_t)(((rand_t)1 << ((sizeof(rand_t) * 8) - 1)) - 1)
typedef unsigned int    rand_t;
 
rand_t my_rand(void)
{
	rand_t rand_left, rand_result;
 
	rand_left = MY_RAND_MAX + 1;
	rand_result = 0;
 
	while( rand_left > 1 )
	{
		rand_result = (rand_result * (RAND_MAX + 1)) + rand();
 
		rand_left /= (RAND_MAX + 1);
	}
 
	return rand_result % (MY_RAND_MAX + 1);
}
 
int main(void)
{
    printf("RAND_MAX: %d\n", RAND_MAX);
    printf("INT_MAX: %d\n", INT_MAX);
 
    srand(time(NULL));
 
    printf("my_rand(): %d\n", my_rand());
 
    return 0;
}

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.