정수 난수 생성기 $RANDOM을 사용하여 특정 정밀도와 특정 범위의 실제 난수를 생성할 수 있습니까? 예를 들어, 0과 1 사이의 정밀도가 4인 실수를 어떻게 생성합니까?
0.1234
0.0309
0.9001
0.0000
1.0000
간단한 해결 방법:
printf "%d04.%d04\n" $RANDOM $RANDOM
답변1
awk -v n=10 -v seed="$RANDOM" 'BEGIN { srand(seed); for (i=0; i<n; ++i) printf("%.4f\n", rand()) }'
n
그러면 [0,1)(예제에서는 10) 범위의 소수점 이하 4자리의 난수가 출력됩니다 . 이는 표준은 아니지만 가장 일반적인 구현으로 구현되는 rand()
in 함수를 사용하여 범위 내에서 임의의 값을 반환합니다. 난수 생성기는 쉘 변수에 의해 시드됩니다.awk
awk
awk
$RANDOM
awk
프로그램에 BEGIN
블록만 있고 다른 코드 블록이 없으면 표준 awk
입력 스트림에서 입력을 읽으려는 시도가 이루어지지 않습니다.
모든 OpenBSD 시스템(또는 동일한 시스템)에서jot
유용, 원래 4.2BSD에 있음) 다음은 지정된 10개의 난수를 생성합니다.
jot -p 4 -r 10 0 1
답변2
다른 답변에서 지적했듯이 난수를 생성하는 데 사용할 수 있는 다른 유틸리티가 있습니다. 이 답변에서는 리소스를 $RANDOM
몇 가지 기본 산술 기능으로 제한합니다.
수레의 경우 다음과 같이 시도하십시오.
printf '%s\n' $(echo "scale=8; $RANDOM/32768" | bc )
$RANDOM
이렇게 하면 0에서 32767 사이의 숫자만 생성되므로 최고의 정확도를 얻을 수 있습니다 . (32767 포함!) 하지만 bc
.
하지만 계속하기 전에 먼저 두 가지 질문을 살펴보고 싶습니다.정확한그리고범위부동 소수점 숫자의 경우. 그런 다음 일련의 정수를 생성하는 것을 고려할 것입니다. (정수를 생성할 수 있는 경우 이를 달성하기 위해 원하는 유틸리티를 사용하려면 나중에 정수를 나누어 소수를 얻을 수 있습니다.)
정확한
사용된 방법은 0부터 32767까지의 값을 생성하므로 결과 도 유한한 개수의 값이 $RANDOM/32768
됩니다 . 즉, 여전히 이산형 확률 변수입니다(컴퓨터를 사용하면 이 사실에서 벗어날 수 없습니다). 이를 염두에 두고 일정 수준의 목표를 달성할 수 있습니다.$RANDOM
$RANDOM/32768
정확한사용하여 printf
.
범위를 더 세부적으로 포괄하려면 기본 32768을 기준으로 생각할 수 있습니다. 따라서 이론적으로는 $RANDOM + $RANDOM*32768
0과 1,073,741,823 사이의 균일한 분포를 제공해야 합니다. 그러나 명령줄이 이런 종류의 정밀도를 잘 처리할 수 있을지 의문입니다. 이 특정 사례와 관련된 몇 가지 사항은 다음과 같습니다.
- 두 개의 독립적이고 균일하게 분포된 확률 변수의 합은 일반적으로 균일하지 않습니다. 이 경우에는 적어도 이론상으로는(3번 항목 참조) 그렇습니다.
- 단순화할 수 있다고 생각하지 마십시오
$RANDOM + $RANDOM*32768 = $RANDOM * ( 1 + 32768 )
. 두 가지 사건은$RANDOM
실제로 두 가지 다른 사건입니다. - 나는 그것을 생성하는 방법을 잘 이해하지 못하며
$RANDOM
이렇게 두 번 호출하면 실제로 두 개의 독립적인 무작위 이벤트가 생성되는지 모르겠습니다.
범위
그것에 대해 생각해 봅시다 $RANDOM/32768
. 숫자 범위를 원하면 이라고 말하면 됩니다 [a,b)
.
$RANDOM/32768*(b-a) + a
원하는 범위 내에 들어갈 것입니다.
정수 값 생성
[0,b)
먼저, 보다 작은 b
사이의 난수 생성을 고려하십시오 32768
. 의 정수 부분인 q*b
제품 을 고려하십시오 . 그러면 당신이 할 수 있는 일은 0에서 32767 사이의 난수를 생성하는 것입니다. 그러나 . 결과 번호로 전화를 겁니다 . 그러면 0 ~ 의 범위에 들어가고 분포가 균일해집니다. 이제 모듈식 연산을 적용하여 이 값을 원하는 범위로 줄입니다.q
32768/b
q*b
G
G
q*b
G % b
무작위로 생성된 숫자는 다음과 같습니다.
$RANDOM % b
b
의 제수 중 하나가 아닌 한 균일 분포를 생성하지 않습니다 32768
.
이를 위해 bash 스크립트를 작성하십시오.
위에서 설명한 계산은 q*b
고통스럽게 들립니다. 그러나 그것은 진실이 아니다. 다음을 통해 얻을 수 있습니다:
q*b = 32768 - ( 32768 % b )
Bash에서는 다음을 사용할 수 있습니다.
$((32768 - $((32768 % b)) ))
0..b
아래 코드는 범위 (제외 ) 내에서 난수를 생성합니다 b
. b=$1
m=$((32768 - $((32768 % $1)) ))
a=$RANDOM
while (( $a > $m ));
do
a=$RANDOM
done
a=$(($a % $1))
printf "$a\n"
부록
기술적으로는 함께 작업할 이유가 거의 없습니다.
m=$((32768 - $((32768 % $1)) ))
다음은 동일한 작업을 수행합니다.
a=$RANDOM
while (( $a > $1 ));
do
a=$RANDOM
done
printf "$a\n"
일이 많은데 컴퓨터가 빨라요.
더 큰 범위의 정수 생성
당신이 알아내도록 할게요. 산술 연산을 처리할 때 컴퓨터의 메모리 제한을 고려해야 하는 경우가 있으므로 주의하세요.
최종 메모
허용되는 답변은 0과 1 사이에서 균일하게 난수를 생성하지 않습니다.
이 콘텐츠를 보려면 다음을 시도해 보세요.
$ for i in {1..1000}; do echo .$RANDOM; done | awk '{ a += $1 } END { print a }'
정말로 균일한 분포를 위해서는 [0,1)
평균이 가까워야 합니다 0.500
.
그러나 위의 코드 조각을 실행하면 알 수 있듯이 314.432
또는 322.619
. 1000개의 숫자가 있으므로 평균은 .322
. 생성된 숫자 시퀀스의 실제 평균은 다음과 같습니다..316362
Perl 스크립트를 사용하여 이 실제 평균을 얻을 수 있습니다.
perl -e '{ $i=0;
$s=0;
while ( $i<=32767 )
{
$j = sprintf "%.5f", ".$i";
$j =~ s/^0\.//;
print "$j\n";
$s += $j;
$i++
};
printf "%.5f\n", $s/32767;
}'
이 사용법이 .$RANDOM
여러분이 원하는 대로 수행되지 않는 이유를 이해하는 데 도움이 되도록 여기에 정수를 추가합니다. 즉, 어떤 정수가 생성되고 어떤 정수가 완전히 손실되는지 생각해 보세요. 꽤 많은 부분이 생략되었습니다. 꽤 많은 부분이 두 배로 늘어났습니다.
답변3
쉘의 printf가 %a
형식(bash ksh zsh 등)을 이해하므로 내부 기본 변경(16진수 -> 10진수)을 수행할 수 있는 시스템에서( [0,1)
0.00003에서 0.99997까지의 통합 범위):
printf '%.5f\n' "$(printf '0x0.%04xp1' $RANDOM)"
더 많은 통화를 결합하면 더 많은 숫자 $RANDOM
(0.000000001~0.999999999) 를 사용할 수도 있습니다.
printf '%.9f\n' "$(printf '0x0.%08xp2' $(( ($RANDOM<<15) + $RANDOM )))"
내부(셸용) "$RANDOM" 알고리즘은 LFSR(선형 피드백 시프트 레지스터)을 기반으로 합니다. 이는 CSPRNG(암호화 보안 의사 난수 생성기)가 아닙니다. 더 나은 옵션은 /dev/urandom
장치의 바이트를 사용하는 것입니다. 이를 위해서는 외부 8진수 또는 16진수 덤프를 호출해야 합니다.
$ printf '%.19f\n' "0x0.$(od -N 8 -An -tx1 /dev/urandom | tr -d ' ')"
0.7532810412812978029
$ printf '%.19f\n' "0x0.$(hexdump -n 8 -v -e '"%02x"' /dev/urandom)"
0.9453460825607180595
부동 소수점 숫자를 얻는 매우 간단한(그러나 고르지 않은) 솔루션은 다음과 같습니다.
printf '0.%04d\n' $RANDOM
범위 내에서 균일하게 만드는 한 가지 방법 [0,1)
(1 제외):
while a=$RANDOM; ((a>29999)); do :; done; printf '0.%04d\n' "$((a%10000))"
답변4
배쉬에서
bc -l <<< "scale=4 ; $((RANDOM % 10000 ))/10000"
1/10000
임의의 정밀도와 숫자는 4
출력 정밀도 입니다.