나눔터  
  HOME > 나눔터 > 묻고답하기 > 엑셀
엑셀
엑셀에 대한 질문과 답변을 올려주세요. 단, 취지에 맞지 않는 글은 운영자가 삭제합니다.
 "000 님, 도와주세요", "부탁 드립니다.", "급합니다!" 등과 같이 막연한 제목을 달지 말아주세요.
[필독] 빠르고 정확한 답변을 얻는 16가지 Tip !
[필독] 저작권법 개정에 따른 이용안내
작성자:  

 조삿갓 (choga21)

추천:  2
파일:     조회:  4200
제목:   [강좌] RND 함수 정복하기
     
  RND 함수(워크시트에서는 RAND 함수임)

1) 기능: 추첨배정, 게임 등을 목적으로 ‘예측할 수 없는 숫자(난수)’를 하나 제공합니다.

2) 반환 값: 그 값은 0이상 1미만의 실수 값입니다.

3) 제한조건: 이 함수는 내부적 원리로는 ‘점화수열’의 특정 항을 내주는 것인데,
   시스템 특성 상 그 점화수열의 초항이 항상 같은 값으로 시작하는 바람에,
   특별한 조치를 취하지 않으면
   매 번 순서대로 발생되는 난수는 늘 같은 값이 되어 버립니다.
   즉, 포커로 치자면 딜러가 게이머들에게 패를 항상 보여주면서 나누어 주는 격이 되지요.
   따라서 실제로 사용하려면 난수를 부르기 전에 먼저
   초항 값을 예측 불허의 값으로 설정해 주어야 합니다.
   이 설명이 무슨 말인지 잘 모르겠어도 좌우간,
   난수를 쓰려면 VBA의 경우 해당 프로시저의 첫 줄에
      RANDOMIZE TIMER
   라는 명령 한 줄을 넣어주면 됩니다.

4) 기본 활용법: 그런데 우리가 필요한 실제 난수는 대개 어떤 범위의 정수 값입니다.
   예를들어 가위바위보를 구현하려면 1~3, 주사위는 1~6,
   고도리는 1~48인데 조커를 더 넣는다면 1~51 정도가 되겠지요.
   그 방법은 다음과 같이 사용하면 됩니다.
   주사위를 예로들면 일단 Rnd() * 6을 해주면 0이상  6미만의 실수 값이 나오겠지요.
   그 값에 Int 함수를 적용하면 소수 이하 꼬리를 잘라버리고 0부터 5까지의 정수가 됩니다.
   거기에 최솟값 1을 더해주면 1부터 6까지의 정수로 되겠지요.
   이 과정을 일반적인 수식으로 쓰면 다음과 같습니다.
     Int(Rnd() * (최댓값 + 1 – 최솟값)) + 최솟값

5) 여기서 잠깐! 왜 최댓값 – 최솟값에 1을 더해 주어야 할까요?
   그 이유는 Rnd 함수의 특성이 이상~미만의 범위 값을 갖기 때문입니다.
   예를 들어, 1~3까지의 값 즉, 1이상 3이하의 값을 찾으려면
   1이상 4미만의 값을 발생시켜야 합니다.
   그러니까 사실은 3-1이 아닌 4-1로 계산해야 하는 것입니다.

6) 골치가 좀 아프지요? 물론 워크시트 함수 중에서
   RANDBETWEEN 함수는 이 일을 간단히 해결할 수도 있습니다.
   워크시트에서 사용할 때에는
     =RANDBETWEEN(최솟값, 최댓값)
   과 같이 사용하면 되고, VBA에서 코딩할 때에는
     WorksheetFunction.RandBetween(최솟값, 최댓값)
   과 같이 사용하면 됩니다만,
   저의 개인적인 취향으로는 WorksheetFunction 개체를 VBA에서 빌려다 쓰는 것은
   왠지 좀 회피하고 있습니다.(타이핑하기도 거시기하고...)

7) 좀 더 깊은 활용법: 꼭 언제나 정수값만 필요한 것은 아니지요.
   이정열님의 의도처럼 소수 아래 첫째 자리까지 값을 갖는
   일정 범위의 실수 값을 요구할 수도 있습니다.
   그러자면 조금 더 어려운 수학적인 가공을 해야 하지요.
  ① 예를들어 20이상 25미만의 범위에서 소수 첫째 자리까지 값을 갖는 난수를 원한다면
     200이상 250미만의 정수를 발생시킨 다음에, 그것을 10으로 나누어주면 됩니다.
        Int(Rnd() * (250 – 200)) / 10 + 20
     어? 4)항의 공식에 있던 + 1 은 어디로 갔냐고요?
     4)항의 공식은 (최솟값)이상 (최댓값)이하인 경우이고,
     여기서는 200이상 250미만이니까요. 5)항의 설명을 다시 복습해 보세요.
     맨 뒤에 더해주는 수가 왜 200이 아니고 20이냐고요?
     실제로 최솟값은 200이 아닌 20이지요.
     Int 함수 안에서 200이 된 이유는 소수 첫째자리까지 발생시키기 위한 기법이고
     10으로 나눈 결과 0이상 50미만의 소수 첫째자리 수까지 갖는 값이 되어 있겠죠.
     여기에 최솟값 20을 더하면 원하는 결과를 얻을 수 있습니다.
  ② 소수 둘째자리까지를 원한다면?
     2000이상 2500미만의 정수를 발생시켜서 100으로 나누면 되겠지요.
        Int(Rnd() * (2500 – 2000)) / 100 + 20
  ③ 만약 20이상 25이하라고 하면, 즉, 정확히 말해 20.0이상 25.0이하라고 하면 어떨까요?    이걸 고쳐 말하면 20.0이상 25.1미만이 되겠지요.
     즉, 200이상 251미만을 발생시켜 10으로 나누면 되지요.
        Int(Rnd() * (251 – 200)) / 10 + 20

8) 그런데, 여기에서 유용하게 사용하는 Int 함수에는 아주 고약한 병이 하나 있습니다.
   원래 Int 함수의 기능은 실수 값을 넣으면 소수 이하를 잘라버리고(0.99999여도 짤 없음)
   정수 부분만을 반환해주는 함수입니다. 그런데, Int 함수 안에서 수학 계산을 좀 하다보면
   이진수 변환 과정에서 일어나는 무한소수 오차 때문에 재수 없는 경우
   Int(2.999)의 값을 2가 아닌 3으로 반환해버리는 일이 가끔 발생합니다.
   제가 이정열님의 이전 질문에 답변하는 코드를 작성하면서 처음에는 Int 함수를 썼다가
   이런 문제를 발견하였습니다.
   그래서 비슷한 일을 하는 형변환 함수인 CInt를 쓰니 이런 문제는 없어졌지만
   CInt는 Int 함수보다 좀 인심이 좋아서, 소수 이하 수를 반올림 처리합니다.
   즉, Int(2.5)의 결과는 2가 되지만, CInt(2.5)의 결과는 3이 되지요.
   그런데 우리는 이상~미만의 원리에 따라 2.9는 3이 아닌 2가 되어야 하기에
   이렇게 되게 하려면 CInt에 넣기 전에 그 수에서 0.5를 빼 주어야 합니다.
   즉, CInt(2.99)는 3이지만 CInt(2.99-0.5)로 쓰면 2가 나오지요.
  ① CInt(Rnd() * (250 – 200) - 0.5) / 10 + 20
  ② CInt(Rnd() * (2500 – 2000) - 0.5) / 100 + 20
  ③ CInt(Rnd() * (251 – 200) - 0.5) / 10 + 20

9) 이상 내용을 기초로 비로소 이정열님의 질문에 해설할 차례가 되었네요.
   당초의 질문 의도를 분석한 결과
  ① 원하는 7개의 난수 값들이 모두 최솟값과 최댓값은 다르지만
     그 차이는 항상 20이라는 것과
     소수이하 첫째자리까지 값을 가져야 한다는 공통점이 있으므로
     이 공통점을 잘 활용하면 반복루프를 이용하여 효율적인 코드를 작성할 수 있지요.
  ② 차이가 20이면서 소수 첫째 자리까지 구해야 하므로 10을 더 곱해야 하는데
     제가 처음 답변하면서 지적했듯이 이상~이하인지 이상~미만인지가 애매하여
     이상~미만이면 200을, 이상~이하이면 201을 곱해야 할 것이라고 지적했었습니다.
     (위 7)항 설명의 ①과 ③을 비교해 보세요)
     그러면 ‘201은 뭐고’라는 데 대한 답변은 되었지요?
  ③ ‘0.5는 뭔지’에 대한 답변은 위 8)항에서 이미 되었고...
  ④ ‘10은 또 뭔지’에 대한 답변도 7)항에서 설명 되었네요

  ⑤ 이제 하나 남은 문제는 각기 다른 최솟값과 최댓값은 어찌할 것인가 하는 것이죠
     여기서 간격이 동일하기 때문에 최솟값만 처리하면 되지요.
     반복을 돌면서 각기 다른 값을 사용해야 한다면
     번호가 붙은 변수 즉, 배열을 쓰면 됩니다.
  ⑥ rBase = Array(0, 20, 30, 120, 140, 110, 50, 20)
     여기에서 첫 번째 수 0 즉, rBase(0)은 그냥 의미 없이 버리는 수입니다.
     (앞 배열강좌의 2), 3), 5) 설명 참고)
  ⑦ 그 다음 7개의 수치들이 각각 7개의 난수 값에 대한 최솟값을 설정한 것입니다.
     이것을 반복문 안에서 제어변수를 첨자로 사용하여 호출하게 되는 것이지요.

강좌 끝!
 
[불량 게시물 신고]  
이정열 이 글은 저장해놓고 두고두고 익힐때까지 보면 좋을듯 합니다.
정말 마지막 질문 하나만 드리겠습니다.
제가 전에 질문했을때 예를 들어서 적다보니 차이가 20이 난것인데
차이가 20이 아니라면 어떻게 변형을 해야 되나요?
10-13 (17:41)
삭제 ■신고
이정열질문이 아니라 생각을 먼저 해봤어야 했네요
차근차근 다시 읽어보면서 생각해보니 최소값처럼 최대값도 array함수를 써서 적용하면 되겠네요. 맞나(?) ㅎ 조삿갓님의 알기쉬운 해설때무에 응용도 쉽게 할수 있겠네요.
혹시 조삿갓님 개인블로그나 이런 쉬운 강좌 알고 계신곳 있으시면 좌표좀... ㅎ
10-16 (16:17)
삭제 ■신고
조삿갓맞습니다.
제 홈피는 www.choga21.org인데
주로 중학교 수학 관련 자료가 많고
(엑셀도 주로 그 목적으로 사용되는 예제가 많지요)
그 외 VB 동영상 강좌도 있으니 아마 도움이 되실 겁니다.
10-19 (16:42)
삭제 ■신고
        
  

작성일 : 2017-10-12(22:14)
최종수정일 : 2017-10-12(22:14)
 


 ◎ 관련글

  제 목   작성자   날짜
엑셀 vba array함수와 rnd 함수 질문입니다. 이정열 2017-10-12
[강좌] 배열의 기초와 Array 함수 조삿갓 2017-10-12
[강좌] RND 함수 정복하기 조삿갓 2017-10-12