본문 바로가기
[ CODING STUDY ]/》C언어 공부

C언어 8장(랜덤, srand, rand, stdlib, time, 난수)

by MRG 2020. 4. 22.
728x90
반응형

안녕하세요 ^^ 
저번장에 드렸던 숙제 해보셨나요?
표준 입출력에 대해 많을걸 진행해서 조금 어려웠지만 그래도 열심히 해보셨을 거라 생각합니다. ^^
오늘은 코딩에서 많이 사용하게 될 랜덤에 대해 한번 배워보도록 하겠습니다.
먼저 이론적인 부분을 한번 이야기해보겠습니다. 
저도 이부분을 할 때 단순히 함수만 외우고 배웠더니 왜 이렇게 하지라는 의문이 생기더라고요.
이론적인 부분은 다 외우거나 암기하지 않아도 아 그래서 이렇게 사용하는구나 정도만 아셔도 될꺼같습니다.



▣ 먼저 난수(랜덤)가 무엇일까요?

나무 위키에서는

'임의의', '무작위의', '무선적인', '무계획적인', '닥치는 대로' 라는 뜻을 가진 영어단어 Random.

'랜덤하다'라는 것은 보통 어떠한 사건이 규칙성이 보이지 않고 무작위로 발생한다는 것을 의미한다. 원래 잘 안 쓰이던 용어였는데, 현대에 들어 그 쓰임이 활발해졌다.

사실 random이라는 단어는 상당히 다양한 용례를 갖는다. 전체 집단에서 아무거나 하나 '랜덤' 하게 뽑아내는 것에서 착안하여, '평범한', '별 볼 일 없는', '특별할 것 없는' 이라는 뜻을 갖기도 한다. 그러니까, 영어로 a random Korean이라고 하면 그냥 지나가던 평범한 한국인이라는 의미가 된다. 또한 어떤 경우에는 체계적인 계획 없이 '랜덤' 하게 제멋대로 움직인다는 점에서 착안하여, 전체 집단에서 이탈하여 혼자 엉뚱한 곳에서 방황하고 있는 개체를 묘사할 때에도 random이라는 표현을 쓰기도 한다.



이렇게 이야기하고 있습니다. 쉽게 생각하자면 무작위라고 생각하시면 편하겠네요.



▣ 그럼 컴퓨터에서는 랜덤을 어떻게 만들까요?
나무 위키에서는 난수를 발생시키는 수식엔 여러 가지가 있는데, 중앙 제곱법과 합동식 방법 등이 있다. 난수 발생 원리에 대해 자세히 알고싶으면 위키피디아를 참고. 라고 나와있습니다.



▣ 중앙 제곱법이랑 합동식 방법이 무엇일까요?
중앙 제곱법은 나무위키에서 
Xn+1​=(Xn​)2의 가운데 a자리
X는 난수 수열, a는 원하는 자릿수(10진법으로), 시드(X0)는 임의의 a자리인 수[2].
Middle-square method. 폰 노이만이 1949년에 고안한 의사난수법. 생성된 품질이 좋지 않기 때문에 그 당시의 컴퓨터면 몰라도 그 이후 시대에는 웬만하면 아래의 선형합동법을 사용한다.
초기 값이 2345이고, 가운데에서 4자리를 선택하여 5개의 난수를 만드는 경우에는 다음과 같습니다.

 

출저 : 수학동아

이런 식으로 폰 노이만이 이런 알고리즘 방법으로 숫자를 생성하게 컴퓨터한테 프로그래밍했다고 생각해주시면 될꺼같습니다.  하지만 이 경우 결국 같은 숫자가 나올 수 있는 확률이 존재합니다. 그래서 다른 방법도 존재합니다. 그게 바로 선형 합동 법입니다. 





▣ 선형합동법은 나무위키에서 이렇게 이야기하고 있습니다.
선형 합동 생성기(Linear congruential generator, LCG)는 널리 알려진 유사난수 생성기이다. 선형 합동 생성기는 다음 재귀 관계로 정의된 순열 {\displaystyle X_{i}}
따라서 선형 합동 생성기는 다음과 같은 인자들로 유일하게 결정된다.
0 < {\displaystyle m} (나눔수)
0 < {\displaystyle a} (곱함수) < {\displaystyle m}
0 ≤ {\displaystyle c} (더함수) < {\displaystyle m}
0 ≤ {\displaystyle X_{0}} (초기값) < {\displaystyle m}
선형 합동 생성기의 상태는 바로 이전에 생성된 난수이며, 이 난수는 최대 {\displaystyle m}
가지 경우가 있으므로 난수의 주기는 최대 {\displaystyle m}
임이 자명하다. 하지만 대부분의 경우 이 주기는 훨씬 짧으며, 최대 주기를 갖기 위한 필요충분조건은 다음과 같다.
{\displaystyle c}와 {\displaystyle m}이 서로소여야 한다.
{\displaystyle a-1}이 {\displaystyle m}의 모든 소인수로 나눠져야 한다.
{\displaystyle m}이 4의 배수일 경우, {\displaystyle a-1}도 4의 배수여야 한다.
라고 나와있는데 결국 이런공식으로 난수를 생성하네요. 





▣ 메르센 트위스터라고 것도 있습니다.
메르센 트위스터는 엑셀, MATLAB, PHP, Python, R, C++ 등에서 사용하고 있는 난수 생성 알고리즘이며, 1997년에 마츠모토 마코토와 니시무라 다쿠지가 개발한 알고리즘입니다.



▣ 결론적으로 컴퓨터 스스로 랜덤 값을 생성하는 게 아니라 이렇게 인간이 만들어준 알고리즘 공식을 계산하여 만드는 게 바로 난수라는 이야기입니다. 
그렇기 때문에 컴퓨터에 난수는 완전한 랜덤은 없다고 이야기를 합니다.
이제 난수를 만드는 걸 코딩을 해볼 텐데요.
우리가 직접 저 공식들을 활용하여 난수를 만들지 않습니다. 
저 공식을 적용한 코딩 가져와서 우리는 사용만 할 뿐입니다. 너무 걱정하지 마세요.

 

▣ 먼저 이렇게 코딩을 진행해보겠습니다. 
랜덤 함수를 사용하려면 stdlib.h 파일을 포함해야 합니다. lib는 library에 약자입니다.
표준 도서관 아니면 라이브러리를 가져온다고 생각하시면 될꺼같습니다. 



그런 다음에 rand() 함수를 사용하면 랜덤 값이 생성할 수 있습니다. 

당연히 이 rand() 함수 안에서는 이런 코딩들이 적용되어 있습니다.
원형은 rand(void) 이렇습니다. 그렇기 때문에 rand() 괄호 안에 값을 넣지 않습니다.
void는 없다는 의미로 생각하시면 될꺼같습니다.
%100은 무엇일까요? 이건 최댓값을 의미합니다.
이렇게 %100 넣게 되면 0~100까지에 숫자 중에 랜덤으로 지정하겠다는 것입니다. 

 

rand()에 %를 붙이지 않으면 RAND_MAX라는 매크로에 정해져 있는 상수값까지 랜덤으로 지정하게 됩니다.
상수를 배우면서 매크로 상수를 정하는 방법을 기억하실 거라 생각합니다.
상수 RAND_MAX가 rand() 최댓값에 상수인 것이죠
이걸 직접 우리가 이렇게 바꿀 수도 있습니다.
저렇게 기호 상수로도 사용할 수도 있습니다. 



그럼 이제 이렇게 여러 개에 랜덤으로 출력되게 한번 만들어보겠습니다.

 

그럼 여러 가지 숫자가 나오긴 하지만 다시 디버그를 하면 똑같은 숫자가 나오게 됩니다. 이건 랜덤이라고 할 수 없겠죠? 왜 그런 걸까요?



▣ 쉽게 이야기하면 시드 값이 같기 때문입니다.
시드 값은 아까 우리가 보았던 3가지 난수를 만드는 공식들을 보았습니다.
그럼 그 공식에서 연산자와 피연산자들이 있겠죠?
그런데 그 피연산자들에 숫자가 같으면 결과가 어떻게 나올까요?
네 당연히 똑같은 결과가 나오게 됩니다. 
시드값은 난수를 구하는 공식 중에 피연산자 값이라고 생각하면 될꺼같습니다.



그럼 이제 시드 숫자를 바꿔주어야겠죠?

 

그래서 우리는 srand 시드 값을 바꿔주는 함수를 사용할겁니다. 
저 위에 코딩처럼 똑같이 시드값을 바꿔보겠습니다. 그럼 다른 값이 나오죠? 
하지만 디버그 할 때마다 결과는 매번 똑같습니다. 왜 그럴까요??



▣ 네 바로 그 숫자도 우리가 바꿔주지 않는 이상 한 번만 바뀌기 때문입니다.
그럼 여기서 난수를 만든 사람은 어떻게 생각을 했을까요? 네 우리랑 똑같이 컴퓨터가 같은 숫자를 주니깐 고민을 했을 겁니다. 매번 바뀌는 숫자가 무엇이 있을까?
무엇이 있을까요? 쉬지 않고 바뀌는 숫자? 네 바로 그겁니다. 바로 시간입니다.!!!

 

▣ 먼저 시간에 관련된 함수를 가지고 오려면 time.h 헤더 파일이 필요합니다. 
그리고 코딩에서와 같이 time(NULL)로 시간을 t변수에 대입을 하고 출력을 하면 저런 결과가 나옵니다. 
저 함수는 1970년 1월 1일 0시 0분 0초부터 현재까지 경과한 시간을 반환해주는 함수입니다.
저 함수를 활용하면 계속 숫자가 바뀌겠죠? 시간은 계속 흐르니깐요. 컴퓨터에서도요.

 

자 이렇게 srand함수에 time을 넣어주고 printf() 함수를 활용하여 계속 출력을 해보면 다른 결과가 나오는 걸 알 수 있습니다.
그런데 저 코딩에서 몇 가지 의문이 생기죠?
srand에서 왜 unsigned int로 강제 형 변환했을까?
그건 signed int는 약 -21억~+21억까지 표현을 합니다. 4byte이기 때문에 32bit죠? 그럼 표현 범위가 저렇게 계산이 됩니다. 우리가 자료형을 했을 때 배웠습니다. 하지만 우리는 음수가 아닌 양수만 필요합니다. 
그렇기 때문에 unsigned int == 약 0~+42억 이렇게 강제 형 변환했습니다. 
강제 형 변환하는 방법은 이렇게 앞에 (unsigned int) 괄호에 자료형을 넣어서 하면 됩니다.
이 부분도 계속 단계가 지나면서 사용할 때 설명을 하도록 하겠습니다.



▣자 랜덤을 다 만들어보셨나요?
이제 좀 이해가 되셨죠?
제가 위에서 난수란 그리고 여러 공식들을 이야기했지만 저 부분은 우선 이런 게 있구나 사람이 컴퓨터한테 난수를 심어주기 위해서 저런 연산을 해주었구나라고만 생각해주시면 좋을 거 같습니다.
그리고 시드란 무엇인지...
왜 시드를 계속 바꿔야 하는지만 이해하시면 랜덤을 만드시는데 어려움이 없을 거라 생각합니다.
그럼 이제 오늘 배운 걸 활용하여 여러 가지를 만들어볼 수 있겠죠?
아직 조건문과 반복문을 배우지 않아서 로또 생성기 같은 건 조금 어려울 수 있지만 그래도 추천번호를 한번 생성해주는 걸 해보시면 좋을 거 같네요. 숫자가 중복되는 현상도 있을 수 있겠지만 그건 일단 넘어가고 한번 해보세요.
이게 이번 숙제입니다. 
꼭 진행해주세요. 그리고 제 블로그로 공부하는 걸로 끝내시면 안 됩니다.
더 좋은 자료들과 영상들이 있기 때문에 꼭 같이 공부하시면서 하셔야 더 코딩 실력이 정말 향상됩니다.
그럼 다음 장에서 뵙겠습니다. 질문 있으시거나 궁금하신 사항 있으시면 댓글 남겨주세요~~



▣ 포기하지 마세요!!! 저도 했습니다. 파이팅~~

 

728x90
반응형

댓글