참고: $RANDOM 은 bash 내부 함수(상수가 아님)로 0에서 32767사이의 의사난수(pseudorandom)를 리턴합니다. $RANDOM 은 암호화 키를 발생 시키는데 쓸 수 없습니다.
예 9-19. 랜덤한 숫자 만들기
#!/bin/bash # $RANDOM 은 불릴 때마다 다른 무작위 정수 값을 리턴합니다. # 명칭상의 범위(nominal range): 0 - 32767(16 비트 양의 정수). MAXCOUNT=10 count=1 echo echo "$MAXCOUNT 개의 랜덤한 숫자:" echo "-----------------" while [ "$count" -le $MAXCOUNT ] # 10 ($MAXCOUNT) 개의 랜덤 정수 발생. do number=$RANDOM echo $number let "count += 1" # 카운터 증가. done echo "-----------------" # 어떤 범위의 랜덤 값이 필요하다면 '나머지(modulo)' 연산자를 쓰면, # 어떤 수를 나눈 나머지 값을 리턴해 줍니다. RANGE=500 echo number=$RANDOM let "number %= $RANGE" echo "$RANGE 보다 작은 랜덤한 숫자 --- $number" echo # 어떤 값보다 큰 랜덤한 정수가 필요하다면 # 그 값보다 작은 수는 무시하는 테스트 문을 걸면 됩니다. FLOOR=200 number=0 # 초기화 while [ "$number" -le $FLOOR ] do number=$RANDOM done echo "$FLOOR 보다 큰 랜덤한 숫자 --- $number" echo # 상한값과 하한값 사이의 수가 필요하다면 위의 두 테크닉을 같이 쓰면 됩니다. number=0 # 초기화 while [ "$number" -le $FLOOR ] do number=$RANDOM let "number %= $RANGE" # $number 가 $RANGE 안에 들어오게. done echo "$FLOOR 와 $RANGE 사이의 랜덤한 숫자 --- $number" echo # "참"이나 "거짓"중에 하나를 고르도록 할 수도 있습니다. BINARY=2 number=$RANDOM T=1 let "number %= $BINARY" # let "number >>= 14" 더 좋은 분포의 랜덤값을 줍니다. # (마지막 두 비트를 제외하고 모두 오른쪽으로 쉬프트 시킴). if [ "$number" -eq $T ] then echo "TRUE" else echo "FALSE" fi echo # 주사위 던지기를 흉내내 볼까요? SPOTS=7 # 7 의 나머지(modulo)는 0 - 6. DICE=2 ZERO=0 die1=0 die2=0 # 정확한 확률을 위해서 두 개의 주사위를 따로 던집시다. while [ "$die1" -eq $ZERO ] # 주사위에 0은 없죠. do let "die1 = $RANDOM % $SPOTS" # 첫번째 주사위를 굴리고. done while [ "$die2" -eq $ZERO ] do let "die2 = $RANDOM % $SPOTS" # 두번째를 굴리면. done let "throw = $die1 + $die2" echo "두 주사위를 던진 결과 = $throw" echo exit 0 |
RANDOM 이 얼마나 랜덤할까요? 이걸 확인해 보는 가장 좋은 방법은 RANDOM 이 발생 시키는 "랜덤"한 숫자의 분포를 추적하는 스크립트를 만들어 보는 것입니다. 그럼 RANDOM 주사위를 몇 번 던져 봅시다.
예 9-20. RANDOM 으로 주사위를 던지기
#!/bin/bash # RANDOM 이 얼마나 랜덤한가? RANDOM=$$ # 스크립트의 프로세스 ID 를 써서 랜덤 넘버 발생기의 seed를 다시 생성. PIPS=6 # 주사위는 눈이 6개죠. MAXTHROWS=600 # 시간이 남아 돌면 이 숫자를 더 늘려보세요. throw=0 # 던진 숫자. zeroes=0 # 초기화를 안 하면 0이 아니라 널이 되기 때문에 ones=0 # 0으로 초기화 해야 됩니다. twos=0 threes=0 fours=0 fives=0 sixes=0 print_result () { echo echo "ones = $ones" echo "twos = $twos" echo "threes = $threes" echo "fours = $fours" echo "fives = $fives" echo "sixes = $sixes" echo } update_count() { case "$1" in 0) let "ones += 1";; # 주사위에 "0"이 없으니까 이걸 1이라고 하고 1) let "twos += 1";; # 이걸 2라고 하고.... 등등.. 2) let "threes += 1";; 3) let "fours += 1";; 4) let "fives += 1";; 5) let "sixes += 1";; esac } echo while [ "$throw" -lt "$MAXTHROWS" ] do let "die1 = RANDOM % $PIPS" update_count $die1 let "throw += 1" done print_result # RANDOM 이 정말 랜덤하다면 결과 분포는 확실히 고르게 나올 것입니다. # $MAXTHROWS 가 600 일 때는 각각은 100 에서 20 정도의 차이를 두고 분산되어야 합니다. # # 주의할 것은 RANDOM 이 의사난수(pseudorandom) 발생기이기 때문에 # 진짜로 랜덤한 것은 아니라는 점입니다. # 쉬운 연습문제 하나 낼께요. # 이 스크립트를 동전 1000 번 뒤집는 걸로 바꿔보세요. # "앞"이나 "뒤" 중에 하나가 나오겠죠? exit 0 |
위의 예제에서 살펴본 것처럼 RANDOM 발생기가 실행될 때마다 랜덤용 seed를 "다시" 만들어 주는 것이 좋습니다. RANDOM에 같은 seed를 쓰면 같은 순서의 숫자가 반복됩니다(이는 C의 random() 함수의 동작을 그대로 반영해 줍니다).
예 9-21. RANDOM 에 seed를 다시 지정해 주기
#!/bin/bash # seeding-random.sh: RANDOM 변수에 seed 적용. MAXCOUNT=25 # 발생시킬 숫자 갯수. random_numbers () { count=0 while [ "$count" -lt "$MAXCOUNT" ] do number=$RANDOM echo -n "$number " let "count += 1" done } echo; echo RANDOM=1 # 랜덤 넘버 발생기에 RANDOM seed 세팅. random_numbers echo; echo RANDOM=1 # 똑같은 seed 사용.... random_numbers # ... 완전히 똑같은 수열이 발생. echo; echo RANDOM=2 # 다른 seed 로 재시도... random_numbers # 다른 수열 발생. echo; echo # RANDOM=$$ 라고 하는 것은 RANDOM 에 스크립트의 프로세스 ID로 seed를 세팅하는 것입니다. # 'time' 이나 'date'로 할 수도 있겠네요. # 더 멋지게 해 보죠. SEED=$(head -1 /dev/urandom | od -N 1 | awk '{ print $2 }') # /dev/urandom(시스템에서 제공하는 의사난수 "디바이스")에서 얻은 의사난수 # 그 다음 "od"를 이용해서 출력 가능한(8진수) 숫자로 이루어진 줄로 변환. # 마지막으로 "awk"를 써서 SEED 에서 쓸 한 개의 숫자를 뽑아냄. RANDOM=$SEED random_numbers echo; echo exit 0 |