Số ngẫu nhiên từ một phạm vi trong Bash Script


196

Tôi cần tạo một số cổng ngẫu nhiên giữa 2000-65000một tập lệnh shell. Vấn đề là số $RANDOM15 bit, vì vậy tôi bị kẹt!

PORT=$(($RANDOM%63000+2001)) sẽ hoạt động tốt nếu nó không giới hạn kích thước.

Có ai có một ví dụ về cách tôi có thể làm điều này, có thể bằng cách trích xuất một cái gì đó từ /dev/urandomvà lấy nó trong một phạm vi?

Câu trả lời:


398
shuf -i 2000-65000 -n 1

Thưởng thức!

Chỉnh sửa : Phạm vi bao gồm.


7
Tôi nghĩ shuflà tương đối gần đây - tôi đã thấy nó trên các hệ thống Ubuntu trong vài năm qua nhưng không phải là RHEL / CentOS hiện tại.
Cascabel

5
Ngoài ra, nó có thể tốt cho việc sử dụng này, nhưng tôi tin rằng shufthực sự thấm vào toàn bộ đầu vào. Điều này làm cho nó trở thành một lựa chọn tồi nếu bạn tạo ra các số ngẫu nhiên rất thường xuyên.
Cascabel

3
@Jefromi: Trên hệ thống của tôi, sử dụng thử nghiệm này time for i in {1..1000}; do shuf -i 0-$end -n 1000 > /dev/null; donevà so sánh end=1để end=65535cho thấy sự cải thiện 25% cho phạm vi ngắn hơn, chênh lệch khoảng 4 giây so với một triệu lần lặp. Và đó là rất nhiều nhanh hơn so với thực hiện tính toán Bash của OP một triệu lần.
Tạm dừng cho đến khi có thông báo mới.

8
@Dennis Williamson: Chạy thử nghiệm của bạn với -n 1sự khác biệt về thời gian không đáng kể, ngay cả với end=4000000000. Những điều tốt để biết shufhoạt động thông minh, không khó :-)
leedm777

6
tôi không có shuf trên máy mac của mình :(
Viren

79

Trên Mac OS X và FreeBSD, bạn cũng có thể sử dụng jot:

jot -r 1  2000 65000

5
Trong ví dụ này, jotcó phân phối không công bằng cho tối thiểu và tối đa của khoảng (ví dụ: 2000 và 65000). Nói cách khác, min và max sẽ được tạo ra ít thường xuyên hơn. Xem câu trả lời của tôi để biết chi tiết và một cách giải quyết.
Clint Pachl

jotcũng có sẵn trong hầu hết các bản phân phối GNU / Linux
Thor

43

Theo trang bash man, $RANDOMđược phân phối từ 0 đến 32767; đó là giá trị 15 bit không dấu. Giả sử $RANDOMđược phân phối đồng đều, bạn có thể tạo một số nguyên 30 bit không dấu được phân phối đồng đều như sau:

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

Vì phạm vi của bạn không phải là công suất 2, nên một thao tác modulo đơn giản sẽ gần như cung cấp cho bạn phân phối đồng đều, nhưng với phạm vi đầu vào 30 bit và phạm vi đầu ra nhỏ hơn 16 bit, như trong trường hợp của bạn, điều này thực sự cần đủ gần:

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

1
biến $RANDOMkhông phải lúc nào cũng có sẵn trong tất cả các shell. Tìm kiếm một giải pháp khác
Lukas Liesis

Nếu tôi hiểu điều này một cách chính xác, bạn đang lan truyền 32.000 số trong phạm vi 1.000.000.000. Nhưng chúng sẽ chỉ đạt được bội số của 2 ^ 15 Bạn đang bỏ qua đếm 2 ^ 15, không điền vào tất cả các chữ số trong khoảng từ 1 đến 2 ^ 30, đó là phân phối đồng đều.
đẳng cấu

@isomorphismes Lưu ý rằng mã tham chiếu $RANDOMhai lần. Trên các shell hỗ trợ $RANDOM, một giá trị mới được tạo ra mỗi khi nó được tham chiếu. Vì vậy, mã này lấp đầy các bit từ 0 đến 14 bằng một $RANDOMgiá trị & điền các bit từ 15 đến 29 bằng một giá trị khác. Giả sử $RANDOMlà đồng nhất & độc lập, điều này bao gồm tất cả các giá trị từ 0 đến 2 ** 30-1 mà không bỏ qua bất cứ điều gì.
Jesin

41

và đây là một với Python

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

và một với awk

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

6
Điều này nhận được một upvote từ tôi. Tôi viết các tập lệnh bash cho các hệ thống khác nhau và tôi tin rằng awk có lẽ là công cụ phong phú nhất cho công việc. Hoạt động trên mac os x và centos mà không gặp vấn đề gì và tôi biết nó cũng sẽ hoạt động trên máy debian của tôi, và có lẽ là bất kỳ máy bình thường nào khác.
John Hunt

6
Tuy nhiên, hạt giống ngẫu nhiên của awk dường như chỉ làm mới một lần / giây nên bạn có thể muốn a) tránh bằng mọi giá hoặc b) khởi tạo lại hạt giống.
John Hunt

+1 vì đây dường như là khả năng POSIX duy nhất mà không cần biên dịch: RANDOMkhông được đảm bảo bởi POSIX,
Ciro Santilli 郝海东 冠状 病 六四 事件

Sử dụng -Stùy chọn kết quả trong ImportError: No module named random. Hoạt động nếu tôi loại bỏ điều đó. Không chắc ý định của ghostdog là gì cho việc đó.
Chris Johnson

1
python -S -c "import random; print random.randrange(2000,63000)"dường như làm việc tốt Tuy nhiên, khi tôi cố gắng lấy một số ngẫu nhiên trong khoảng từ 1 đến 2, tôi dường như luôn nhận được 1 ... Suy nghĩ?
Hubert Léveillé Gauvin

17

Cách chung đơn giản nhất mà bạn nghĩ đến là một perl liner:

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

Bạn luôn có thể sử dụng hai số:

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

Bạn vẫn phải clip vào phạm vi của bạn. Đây không phải là một phương pháp số ngẫu nhiên n-bit chung, nhưng nó sẽ hoạt động cho trường hợp của bạn và tất cả nằm trong bash.

Nếu bạn muốn thực sự dễ thương và đọc từ / dev / urandom, bạn có thể làm điều này:

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

Điều đó sẽ đọc hai byte và in chúng dưới dạng int unsign; bạn vẫn phải làm việc của bạn


Tôi đã sử dụng kỹ thuật này và nhận thấy rằng bây giờ và sau đó sẽ không có số được tạo, chỉ đơn giản là không gian trống.
PdC

Nó yêu cầu perl cài đặt. Tôi viết một kịch bản nên chạy trên hầu hết nếu không phải tất cả các máy linux, gắn bó với awkphiên bản từ một câu trả lời khác
Lukas Liesis

Thêm số ngẫu nhiên ủng hộ kết quả trung bình với chi phí thấp hoặc cao. Nó không phải là ngẫu nhiên thống nhất.
đẳng cấu

@isomorphismes Có, nếu bạn thực sự chỉ cần thêm hai số ngẫu nhiên. Nhưng, giả sử bạn đang đề cập đến biểu thức thứ hai ở đây, đó không phải là điều đang làm. Đó là một số ngẫu nhiên trong [0,32767] cộng với lựa chọn ngẫu nhiên độc lập cho bit tiếp theo, tức là 0 hoặc 32768. Đó là thống nhất. (Nó không lý tưởng cho câu hỏi ban đầu vì bạn phải cắt phạm vi bằng cách đọc lại.)
Cascabel

7

Nếu bạn không phải là chuyên gia bash và đang tìm cách biến điều này thành một biến trong tập lệnh bash dựa trên Linux, hãy thử điều này:

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

Điều đó giúp bạn có phạm vi từ 200 đến 700 $VAR, bao gồm.


5

Đây là một số khác. Tôi nghĩ rằng nó sẽ hoạt động trên bất cứ thứ gì, nhưng tùy chọn ngẫu nhiên sắp xếp không có sẵn trên hộp centos của tôi tại nơi làm việc.

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

3
sort -Rkhông có sẵn trên OS X.
Lri

5

$RANDOMlà một số trong khoảng từ 0 đến 32767. Bạn muốn có một cổng trong khoảng từ 2000 đến 65000. Đây là 63001 cổng có thể. Nếu chúng tôi tuân theo các giá trị trong $RANDOM + 2000khoảng từ 2000 đến 33500 , chúng tôi sẽ bao gồm một phạm vi gồm 31501 cổng. Nếu chúng ta lật một đồng xu và sau đó có điều kiện thêm 31501 vào kết quả, chúng ta có thể nhận được nhiều cổng hơn, từ 33501 đến 65001 . Sau đó, nếu chúng ta giảm 65001, chúng ta sẽ có được phạm vi bảo hiểm chính xác, với phân phối xác suất thống nhất cho tất cả các cổng, có vẻ như vậy.

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
}

Kiểm tra

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

Tương tự với ruby:

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

3

Tài liệu Bash nói rằng mỗi lần $RANDOMđược tham chiếu, một số ngẫu nhiên trong khoảng từ 0 đến 32767 được trả về. Nếu chúng tôi tổng hợp hai tham chiếu liên tiếp, chúng tôi nhận được các giá trị từ 0 đến 65534, bao gồm phạm vi mong muốn là 63001 khả năng cho một số ngẫu nhiên trong khoảng từ 2000 đến 65000.

Để điều chỉnh nó đến phạm vi chính xác, chúng tôi sử dụng tổng modulo 63001, sẽ cung cấp cho chúng tôi một giá trị từ 0 đến 63000. Điều này đến lượt nó chỉ cần tăng thêm 2000 để cung cấp số ngẫu nhiên mong muốn, trong khoảng từ 2000 đến 65000. Điều này có thể là tóm tắt như sau:

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

Kiểm tra

# 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

Tính chính xác của phép tính

Dưới đây là một bài kiểm tra đầy đủ, vũ phu cho tính chính xác của phép tính. Chương trình này chỉ cố gắng tạo ngẫu nhiên tất cả 63001 khả năng khác nhau, sử dụng phép tính được kiểm tra. Các --jobstham số nên làm cho nó chạy nhanh hơn, nhưng nó không xác định (tổng cộng khả năng tạo ra có thể thấp hơn 63.001).

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

Để xác định có bao nhiêu lần lặp là cần thiết để có xác suất nhất định cho p/qtất cả 63001 khả năng đã được tạo, tôi tin rằng chúng ta có thể sử dụng biểu thức bên dưới. Ví dụ, đây là phép tính cho xác suất lớn hơn 1/2ở đây lớn hơn 9/10 .

Biểu hiện


1
Bạn sai rồi. $RANDOMmột số nguyên . Với "mánh khóe" của bạn, có nhiều giá trị sẽ không bao giờ đạt được. -1.
gniourf_gniourf

2
Tôi không chắc ý của bạn là "là số nguyên", nhưng đúng, thuật toán đã sai. Nhân một giá trị ngẫu nhiên từ một phạm vi giới hạn sẽ không làm tăng phạm vi. $RANDOMThay vào đó, chúng ta cần tổng hợp hai quyền truy cập và không tái cấu trúc số đó thành phép nhân với hai, vì $RANDOMđược cho là thay đổi trên mỗi lần truy cập. Tôi đã cập nhật câu trả lời với phiên bản tổng.

6
Làm RANDOM+RANDOMsẽ không cung cấp cho bạn một phân phối thống nhất các số ngẫu nhiên trong khoảng từ 0 đến 65534.
gniourf_gniourf

3
Đúng, nói cách khác, không phải tất cả các khoản tiền đều có cơ hội như nhau. Trên thực tế, nó xì hơi từ đó, nếu chúng ta kiểm tra biểu đồ thì đó là một kim tự tháp! Tôi nghĩ đây là lý do tại sao tôi đã nhận được thời gian tính toán lớn hơn đáng kể so với thời gian dự kiến ​​theo công thức trên. Cũng có một vấn đề với hoạt động modulo: các khoản tiền từ 63001 đến (32767 + 32767) tăng gấp đôi khả năng xảy ra cho 2534 cổng đầu tiên so với các cổng còn lại. Tôi đã suy nghĩ về các lựa chọn thay thế, nhưng tôi nghĩ tốt hơn là nên bắt đầu lại từ đầu bằng một câu trả lời mới, vì vậy tôi sẽ bỏ phiếu này để xóa.

4
Nó giống như lăn 2 con xúc xắc sáu mặt. Theo thống kê, nó cung cấp cho bạn một đường cong hình chuông: xác suất thấp để cán "2" hoặc "12", với xác suất cao nhất nhận được "7" ở giữa.
Ogre Psalm33

2

Hoặc trên OS-X, các công việc sau đây đối với tôi:

$ gsort --random-sort

2

PORT=$(($RANDOM%63000+2001)) gần với những gì bạn muốn tôi nghĩ.

PORT=$(($RANDOM$RANDOM$RANDOM%63000+2001))được giới hạn kích thước mà làm phiền bạn. Vì bash không phân biệt giữa biến số và biến chuỗi, nên điều này hoạt động hoàn toàn tốt. "Số" $RANDOMcó thể được nối như một chuỗi, và sau đó được sử dụng như một số trong phép tính. Kinh ngạc!


1
Tôi thấy những gì bạn đang nói. Tôi đồng ý phân phối sẽ khác nhau, nhưng dù sao bạn cũng không thể có được sự ngẫu nhiên thực sự. Có thể tốt hơn khi đôi khi sử dụng $ RANDOM, đôi khi $ RANDOM $ RANDOM và đôi khi $ RANDOM $ RANDOM $ RANDOM để có được phân phối đồng đều hơn. Nhiều RANDOM hơn ủng hộ số cổng cao hơn, theo như tôi có thể nói.
Wastrel

(Tôi đã xóa nhận xét ban đầu của mình, vì tôi đã sử dụng một số giá trị số sai và đã quá muộn để chỉnh sửa nhận xét). Đúng. x=$(( $n%63000 )gần giống với x=$(( $n % 65535 )); if [ $x -gt 63000 ]; then x=63000.
chepner

Tôi sẽ không chỉ trích (hoặc thậm chí làm) toán học. Tôi chỉ đơn giản là chấp nhận nó. Đây là những gì tôi muốn nói: num = ($ RANDOM $ RANDOM $ RANDOM $ RANDOM $ RANDOM $ RANDOM); chọn = $ (($ RANDOM% 3)); PORT = $ (($ {num [$ pick]}% 63000 + 2001)) --- có vẻ như rất nhiều rắc rối ...
Wastrel

1

Bạn có thể nhận được số ngẫu nhiên thông qua urandom

head -200 /dev/urandom | cksum

Đầu ra:

3310670062 52870

Để lấy một phần của số trên.

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

Sau đó, đầu ra là

3310670062

Để đáp ứng yêu cầu của bạn,

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


0

Đây là cách tôi thường tạo số ngẫu nhiên. Sau đó, tôi sử dụng "NUM_1" làm biến số cho số cổng tôi sử dụng. Đây là một kịch bản ví dụ ngắn.

#!/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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.