Bash: $RANDOM을 사용하여 임의의 부동 소수점 숫자를 생성하는 방법

Bash: $RANDOM을 사용하여 임의의 부동 소수점 숫자를 생성하는 방법

정수 난수 생성기 $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 함수를 사용하여 범위 내에서 임의의 값을 반환합니다. 난수 생성기는 쉘 변수에 의해 시드됩니다.awkawkawk$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*327680과 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 ~ 의 범위에 들어가고 분포가 균일해집니다. 이제 모듈식 연산을 적용하여 이 값을 원하는 범위로 줄입니다.q32768/bq*bGGq*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출력 정밀도 입니다.

관련 정보