Bash 스크립트의 범위에서 난수


198

2000-65000쉘 스크립트에서 임의의 포트 번호를 생성해야합니다 . 문제는 $RANDOM15 비트 숫자이므로 멈췄습니다!

PORT=$(($RANDOM%63000+2001)) 크기 제한이 없다면 멋지게 작동합니다.

누구나 내가 어떻게 할 수 있는지에 대한 예를 가지고 있습니까? 어쩌면 무언가를 추출 /dev/urandom하여 범위 내에서 가져 오는 것입니까?

답변:


398
shuf -i 2000-65000 -n 1

즐겨!

편집 : 범위가 포함됩니다.


7
나는 shuf비교적 최근에 생각합니다 -지난 몇 년 동안 우분투 시스템에서 보았지만 현재 RHEL / CentOS는 아닙니다.
— Cascabel

5
또한이 용도로 shuf는 괜찮을지 모르지만 실제로는 전체 입력을 순화 시킵니다. 난수를 매우 자주 생성하는 경우 나쁜 선택입니다.
— Cascabel

3
@Jefromi : 내 시스템에서이 테스트를 사용 time for i in {1..1000}; do shuf -i 0-$end -n 1000 > /dev/null; done하고 비교 end=1에 end=65535만 반복 이상 4에 대한 초 차이 금액은 짧은 범위에 대한 25 %의 개선을 보여 주었다. 그리고 그건 많이 빠르게 OP의 배쉬 계산 만 번을 수행하는 것보다.
— 추후 공지가있을 때까지 일시 중지되었습니다.

9
@Dennis Williamson :로 테스트를 실행 -n 1해도 시간 차이는 무시할만한 수준으로 나타났습니다 end=4000000000. 알아두면 좋은 shuf점은 똑똑하지 않고 잘 작동 함 :-)
— leedm777

6
나는 내 맥에 uf하지
— 않았다

79

Mac OS X 및 FreeBSD에서는 jot을 사용할 수도 있습니다.

jot -r 1  2000 65000

5
이 예에서는 jot구간의 최소값과 최대 값 (예 : 2000 및 65000)에 대한 불균형 분포가 있습니다. 즉, 최소 및 최대가 덜 자주 생성됩니다. 자세한 내용과 해결 방법 은 내 답변 을 참조하십시오 .
— 클린트 Pachl

jot대부분의 GNU / Linux 배포판에서도 사용 가능
— Thor

43

bash 매뉴얼 페이지에 따르면 $RANDOM0에서 32767 사이에 배포됩니다. 즉, 부호없는 15 비트 값입니다. $RANDOM균일하게 분포되어 있다고 가정하면 다음과 같이 균일하게 분포 된 부호없는 30 비트 정수를 만들 수 있습니다.

$(((RANDOM<<15)|RANDOM))

범위가 2의 거듭 제곱이 아니기 때문에 간단한 모듈로 연산은 거의 균일 한 분포를 제공하지만 30 비트 입력 범위와 16 비트 미만의 출력 범위를 가지고 있습니다. 이것은 실제로 충분히 가까워 야합니다.

PORT=$(( ((RANDOM<<15)|RANDOM) % 63001 + 2000 ))

1
변수 $RANDOM가 모든 쉘에서 항상 사용 가능한 것은 아닙니다. 또 다른 솔루션을 찾고
— 루카스 Liesis

내가 이것을 올바르게 이해한다면, 당신은 1,000,000,000의 범위에서 32,000의 숫자를 퍼 뜨리고 있습니다. 그러나 2 ^ 15의 배수에만 적용됩니다. 1에서 2 ^ 30 사이의 모든 숫자를 균등하게 채우지 않고 2 ^ 15로 건너 뛰기 때문에 균일 한 분포입니다.
— 동 형사상

@isomorphismes 코드는 $RANDOM두 번 참조 합니다. 를 지원 $RANDOM하는 쉘에서는 참조 될 때마다 새 값이 생성됩니다. 따라서이 코드는 비트 0에서 14까지 하나의 $RANDOM값으로 채우고 비트 15에서 29까지는 다른 값으로 채 웁니다. $RANDOM균일하고 독립적 이라고 가정하면 아무것도 건너 뛰지 않고 0에서 2 ** 30-1까지의 모든 값을 포괄합니다.
— Jesin

41

여기에 파이썬이 있습니다

randport=$(python -S -c "import random; print random.randrange(2000,63000)")

하나는 awk

awk 'BEGIN{srand();print int(rand()*(63000-2000))+2000 }'

6
이 사람은 나에게서 공감대를 얻는다. 다양한 시스템에 대한 bash 스크립트를 작성하고 awk가 아마도 작업에 가장 풍부한 도구라고 생각합니다. Mac OS X 및 CentOS에서 문제없이 작업했으며 데비안 시스템과 다른 일반적인 * nix 시스템에서도 작동한다는 것을 알고 있습니다.
— John Hunt

6
그러나 awk의 무작위 시드는 초당 1 회만 새로 고침되므로 a) 모든 비용을 피하거나 b) 시드를 다시 초기화 할 수 있습니다.
— 존 헌트

+1이 편집없이 유일한 POSIX 가능성 것으로 보인다 이유는 RANDOM, POSIX에 의해 보장되지 않습니다
— 치로 틸리郝海东冠状病六四事件法轮功

-S옵션을 사용하면 결과가 나타납니다 ImportError: No module named random. 내가 제거하면 작동합니다. 고스트 독의 의도가 무엇인지 확실하지 않습니다.
— 크리스 존슨

1
python -S -c "import random; print random.randrange(2000,63000)"잘 작동하는 것 같습니다. 그러나 1과 2 사이의 난수를 얻으려고 할 때 항상 1을 얻는 것 같습니다 ... 생각?
— Hubert Léveillé Gauvin

17

가장 단순한 일반적인 방법은 펄 원 라이너입니다.

perl -e 'print int(rand(65000-2000)) + 2000'

항상 두 개의 숫자를 사용할 수 있습니다.

PORT=$(($RANDOM + ($RANDOM % 2) * 32768))

여전히 범위로 클립해야합니다. 일반적인 n 비트 난수 방법은 아니지만 귀하의 경우에는 효과가 있으며 모두 bash 안에 있습니다.

정말 귀엽고 / dev / urandom에서 읽으려면 다음을 수행하십시오.

od -A n -N 2 -t u2 /dev/urandom

두 바이트를 읽고 부호없는 int로 인쇄합니다. 당신은 여전히 ​​클리핑을해야합니다.


나는이 기술을 사용하여 지금은 숫자가 생성되지 않고 단순히 공백이 있음을 알았습니다.
— PdC

펄이 설치되어 있어야합니다. 나는 모든 리눅스 머신이 아니라면 대부분의 스크립트 awk에서 다른 답변의 버전을 고수해야한다
— Lukas Liesis

난수를 추가하면 낮거나 높은 비용으로 중간 결과를 선호합니다. 균일하지 않습니다.
— 동 형사상

@isomorphismes 예, 문자 그대로 두 개의 난수를 추가하는 경우 가능합니다. 그러나 여기서 두 번째 표현을 언급한다고 가정하면, 그것이하는 것이 아닙니다. [0,32767]의 난수와 다음 비트 (예 : 0 또는 32768)에 대한 독립적 인 임의의 선택입니다. 균일합니다. (재 롤링하여 범위를 클립해야하기 때문에 원래 질문에는 이상적이지 않습니다.)
— Cascabel

7

bash 전문가가 아니며 Linux 기반 bash 스크립트에서 변수로 가져 오려면 다음을 시도하십시오.

VAR=$(shuf -i 200-700 -n 1)

200에서 700까지의 범위를 $VAR포함합니다.


5

여기 또 다른 것이 있습니다. 나는 그것이 거의 무엇이든 작동 할 것이라고 생각했지만 직장의 centos 상자에서 sort의 임의 옵션을 사용할 수 없습니다.

 seq 2000 65000 | sort -R | head -n 1

3
sort -ROS X에서도 사용할 수 없습니다.
— Lri

5

$RANDOM0과 32767 사이의 숫자입니다. 2000과 65000 사이의 포트를 원합니다. 63001 개의 가능한 포트입니다. 2000 에서 33500$RANDOM + 2000 사이의 값을 고수하면 31501 포트 범위를 포괄합니다. 동전을 뒤집은 다음 조건부로 결과에 31501을 추가하면 33501 에서 65001 까지 더 많은 포트를 얻을 수 있습니다 . 그런 다음 65001 만 떨어 뜨리면 모든 포트에 대해 균일 한 확률 분포로 필요한 정확한 범위를 얻게됩니다.

random-port() {
    while [[ not != found ]]; do
        # 2000..33500
        port=$((RANDOM + 2000))
        while [[ $port -gt 33500 ]]; do
            port=$((RANDOM + 2000))
        done

        # 2000..65001
        [[ $((RANDOM % 2)) = 0 ]] && port=$((port + 31501)) 

        # 2000..65000
        [[ $port = 65001 ]] && continue
        echo $port
        break
    done
}

테스팅

i=0
while true; do
    i=$((i + 1))
    printf "\rIteration $i..."
    printf "%05d\n" $(random-port) >> ports.txt
done

# Then later we check the distribution
sort ports.txt | uniq -c | sort -r


5

루비와 동일 :

echo $(ruby -e 'puts rand(20..65)') #=> 65 (inclusive ending)
echo $(ruby -e 'puts rand(20...65)') #=> 37 (exclusive ending)

3

Bash 문서에 따르면$RANDOM 참조 될 때마다 0에서 32767 사이의 임의의 숫자가 반환됩니다. 두 개의 연속 참조를 합하면 0에서 65534 사이의 값을 얻습니다. 2000에서 65000 사이의 난수에 대한 63001 가능성의 범위를 포괄합니다.

정확한 범위로 조정하기 위해 합 모듈로 63001을 사용하여 0에서 63000 사이의 값을 제공합니다. 2000에서 65000 사이의 원하는 임의의 숫자를 제공하려면 2000 씩 증가하면됩니다. 다음과 같이 요약됩니다 :

port=$((((RANDOM + RANDOM) % 63001) + 2000))

테스팅

# Generate random numbers and print the lowest and greatest found
test-random-max-min() {
    max=2000
    min=65000
    for i in {1..10000}; do
        port=$((((RANDOM + RANDOM) % 63001) + 2000))
        echo -en "\r$port"
        [[ "$port" -gt "$max" ]] && max="$port"
        [[ "$port" -lt "$min" ]] && min="$port"
    done
    echo -e "\rMax: $max, min: $min"
}

# Sample output
# Max: 64990, min: 2002
# Max: 65000, min: 2004
# Max: 64970, min: 2000

계산의 정확성

다음은 계산의 정확성을위한 완전한 무차별 테스트입니다. 이 프로그램은 테스트중인 계산을 사용하여 모든 63001 가지 가능성을 무작위로 생성하려고합니다. 이 --jobs매개 변수는 더 빨리 실행되도록해야하지만 결정적이지는 않습니다 (생성 된 총 가능성이 63001보다 낮을 수 있음).

test-all() {
    start=$(date +%s)
    find_start=$(date +%s)
    total=0; ports=(); i=0
    rm -f ports/ports.* ports.*
    mkdir -p ports
    while [[ "$total" -lt "$2" && "$all_found" != "yes" ]]; do
        port=$((((RANDOM + RANDOM) % 63001) + 2000)); i=$((i+1))
        if [[ -z "${ports[port]}" ]]; then
            ports["$port"]="$port"
            total=$((total + 1))
            if [[ $((total % 1000)) == 0 ]]; then
                echo -en "Elapsed time: $(($(date +%s) - find_start))s \t"
                echo -e "Found: $port \t\t Total: $total\tIteration: $i"
                find_start=$(date +%s)
            fi
        fi
    done
    all_found="yes"
    echo "Job $1 finished after $i iterations in $(($(date +%s) - start))s."
    out="ports.$1.txt"
    [[ "$1" != "0" ]] && out="ports/$out"
    echo "${ports[@]}" > "$out"
}

say-total() {
    generated_ports=$(cat "$@" | tr ' ' '\n' | \sed -E s/'^([0-9]{4})$'/'0\1'/)
    echo "Total generated: $(echo "$generated_ports" | sort | uniq | wc -l)."
}
total-single() { say-total "ports.0.txt"; }
total-jobs() { say-total "ports/"*; }
all_found="no"
[[ "$1" != "--jobs" ]] && test-all 0 63001 && total-single && exit
for i in {1..1000}; do test-all "$i" 40000 & sleep 1; done && wait && total-jobs

p/q모든 63001 가능성이 주어진 확률을 얻기 위해 얼마나 많은 반복이 필요한지 결정하기 위해 아래 표현식을 사용할 수 있다고 생각합니다. 예를 들어, 여기의 1/2보다 큰 확률의 계산이고 , 그리고 여기에 9/10보다 큰 대 .

표현


1
네가 틀렸어. $RANDOM인 정수 . 당신의 "속임수"에는 결코 달성 할 수없는 많은 가치들이 있습니다. -1.
— gniourf_gniourf

2
"정수"라는 것이 무엇인지 잘 모르겠지만 알고리즘이 잘못되었습니다. 제한된 범위에서 임의의 값을 곱해도 범위가 증가 하지 않습니다 . 우리는 $RANDOM대신 두 개의 액세스를 합산해야하며 $RANDOM모든 액세스에서 변경되어야하기 때문에 두 개의 곱셈으로 리팩토링하지 마십시오 . 합계 버전으로 답변을 업데이트했습니다.

6
그렇게하면 0에서 65534 사이의 임의의 숫자 RANDOM+RANDOM가 균일하게 분포 되지 않습니다.
— gniourf_gniourf

3
다시 말해, 모든 합계가 똑같이 발생하는 것은 아닙니다. 사실, 그것과는 거리가 멀다. 그래프를 보면 피라미드이다! 이것이 내가 위의 공식에서 예상되는 것보다 상당히 큰 계산 시간을 얻은 이유라고 생각합니다. 모듈로 연산에도 문제가 있습니다. 63001에서 (32767 + 32767)의 합은 나머지 포트와 비교하여 처음 2534 포트에서 발생할 확률을 두 배로 늘립니다. 나는 대안을 생각하고 있지만 새로운 답변으로 처음부터 시작하는 것이 낫다고 생각하므로 삭제를 위해 투표하고 있습니다.

4
육면체 주사위 2 개를 굴리는 것과 같습니다. 통계적으로 그것은 "2"또는 "12"를 굴릴 확률이 낮고 중간에 "7"을 얻을 확률이 높은 종 곡선을 제공합니다.
— 오우거 시편


2

PORT=$(($RANDOM%63000+2001)) 당신이 원하는 것에 가깝습니다.

PORT=$(($RANDOM$RANDOM$RANDOM%63000+2001))당신을 괴롭히는 크기 제한을 극복합니다. bash는 숫자 변수와 문자열 변수를 구별하지 않기 때문에 완벽하게 작동합니다. "숫자" $RANDOM는 문자열처럼 연결 한 다음 계산에서 숫자로 사용할 수 있습니다. 놀랄 만한!


1
당신이 무슨 말을하는지 봅니다. 분포가 다를 것이라는 데 동의하지만 어쨌든 실제 임의성을 얻을 수는 없습니다. 보다 균일 한 배포를 위해 때때로 $ RANDOM, 때로는 $ RANDOM $ RANDOM 및 때로는 $ RANDOM $ RANDOM $ RANDOM을 사용하는 것이 좋습니다. 내가 알 수있는 한 더 많은 $ RANDOM은 더 높은 포트 번호를 선호합니다.
— Wastrel

(잘못된 숫자 값을 사용하여 주석을 편집하기에는 너무 늦었으므로 원래 주석을 삭제했습니다.) 권리. x=$(( $n%63000 )대략 비슷합니다 x=$(( $n % 65535 )); if [ $x -gt 63000 ]; then x=63000.
— chepner

나는 수학을 비판하지 않을 것입니다. 나는 그것을 단순히 받아 들였다. 이것은 내가 의미 한 바이다 : num = ($ RANDOM $ RANDOM $ RANDOM $ RANDOM $ RANDOM $ RANDOM); pick = $ (($ RANDOM % 3)); PORT = $ (($ {num [$ pick]} % 63000 + 2001)) --- 많은 문제가있는 것 같습니다 ...
— Wastrel

1

당신은 난수를 통해 얻을 수 있습니다 urandom

head -200 /dev/urandom | cksum

산출:

3310670062 52870

위 숫자의 한 부분을 검색합니다.

head -200 /dev/urandom | cksum | cut -f1 -d " "

그런 다음 출력은

3310670062

귀하의 요구 사항을 충족시키기 위해

head -200 /dev/urandom |cksum | cut -f1 -d " " | awk '{print $1%63000+2001}'


0

이것이 일반적으로 난수를 생성하는 방법입니다. 그런 다음 사용하는 포트 번호의 변수로 "NUM_1"을 사용합니다. 다음은 간단한 예제 스크립트입니다.

#!/bin/bash

clear
echo 'Choose how many digits you want for port# (1-5)'
read PORT

NUM_1="$(tr -dc '0-9' </dev/urandom | head -c $PORT)"

echo "$NUM_1"

if [ "$PORT" -gt "5" ]
then
clear
echo -e "\x1b[31m Choose a number between 1 and 5! \x1b[0m"
sleep 3
clear
exit 0
fi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.