Làm thế nào để tạo số ngẫu nhiên trong Bash?


235

Làm thế nào để tạo một số ngẫu nhiên trong một phạm vi trong Bash?


21
Làm thế nào ngẫu nhiên nó cần phải được?
bdonlan

Câu trả lời:


283

Sử dụng $RANDOM. Nó thường hữu ích khi kết hợp với số học shell đơn giản. Chẳng hạn, để tạo một số ngẫu nhiên trong khoảng từ 1 đến 10 (đã bao gồm):

$ echo $((1 + RANDOM % 10))
3

Máy phát điện thực tế là trong variables.c , chức năng brand(). Các phiên bản cũ hơn là một máy phát tuyến tính đơn giản. Phiên bản 4.0 bashsử dụng một trình tạo với một trích dẫn đến một bài báo năm 1985, có lẽ có nghĩa là nó là một nguồn số giả ngẫu nhiên. Tôi sẽ không sử dụng nó cho một mô phỏng (và chắc chắn không phải cho tiền điện tử), nhưng có lẽ nó đủ cho các tác vụ kịch bản cơ bản.

Nếu bạn đang làm một cái gì đó yêu cầu số ngẫu nhiên nghiêm trọng, bạn có thể sử dụng /dev/randomhoặc /dev/urandomnếu chúng có sẵn:

$ dd if=/dev/urandom count=4 bs=1 | od -t d

20
Hãy cẩn thận ở đây. Mặc dù điều này là tốt trong một nhúm, thực hiện số học trên các số ngẫu nhiên có thể ảnh hưởng đáng kể đến tính ngẫu nhiên của kết quả của bạn. trong trường hợp $RANDOM % 10, 8 và 9 có thể đo lường được (mặc dù không đáng kể) ít có thể xảy ra hơn 0-7, ngay cả khi đó $RANDOMlà một nguồn dữ liệu ngẫu nhiên mạnh mẽ.
dimo414

3
@ dimo414 Tôi tò mò "bên lề", bạn có nguồn nào để tôi có thể tìm hiểu thêm về điều này không?
PascalVKooten

58
Bằng cách điều chỉnh đầu vào ngẫu nhiên của bạn, bạn sẽ "kết thúc" chim bồ câu . Vì $RANDOMphạm vi là 0-32767các số 0- 7ánh xạ tới 3277các đầu vào có thể khác nhau, nhưng 89chỉ có thể được tạo ra theo 3276các cách khác nhau (vì 3276832769không thể). Đây là một vấn đề nhỏ đối với hack nhanh, nhưng có nghĩa là kết quả không ngẫu nhiên thống nhất. Các thư viện ngẫu nhiên, như của Java Random, cung cấp các hàm để trả về đúng một số ngẫu nhiên thống nhất trong phạm vi đã cho, thay vì chỉ đơn giản là sửa đổi một số không chia được.
dimo414

1
@JFSebastian rất đúng - vấn đề với modulo là nó có thể phá vỡ tính đồng nhất của bất kỳ RNG nào , không chỉ PRNG xấu, mà cảm ơn vì đã gọi nó ra.
dimo414

14
Chỉ cần cho bối cảnh, pigeonholing cơ bản cho% 10 có nghĩa là 8 và 9 có khả năng xảy ra ít hơn 0,03% so với 0 mộc7. Nếu tập lệnh shell của bạn yêu cầu số ngẫu nhiên thống nhất chính xác hơn số đó, thì bằng mọi cách, hãy sử dụng một cơ chế phù hợp và phức tạp hơn.
Nelson

70

Vui lòng xem $RANDOM:

$RANDOM là một hàm Bash nội bộ (không phải là hằng số) trả về số nguyên giả trong phạm vi 0 - 32767. Không nên sử dụng nó để tạo khóa mã hóa.


1
32767ý nghĩa đặc biệt nào không?
Jin Kwon

14
@JinKwon 327672^16 / 2 - 1giới hạn trên cho số nguyên 16 bit đã ký.
Jeffrey Martinez

@JinKwon bạn có thể làm rõ lý do tại sao bạn không nói nó là 2^15 - 1? Nó tương đương nhau, vì vậy tôi chỉ tò mò nếu có một số bối cảnh tôi đang thiếu?
Brett Holman

11
@BrettHolman Tôi nghĩ rằng anh ta đang cố gắng chỉ ra phần "đã ký" của số nguyên 16 bit đã ký. 2 ^ 16 giá trị, chia làm đôi cho các số nguyên và số âm.
cody

Câu trả lời này không trả lời câu hỏi.
nhóc

48

Bạn cũng có thể sử dụng shuf (có sẵn trong coreutils).

shuf -i 1-100000 -n 1

Làm thế nào để bạn vượt qua trong vars như kết thúc phạm vi? Tôi đã nhận được điều này:shuf -i 1-10 -n 1: syntax error in expression (error token is "1-10 -n 1")
dat tutbrus 16/07/18

1
Thêm một $varthay vì kết thúc phạm vi, như thế này:var=100 && shuf -i 1-${var} -n 1
knipwim

2
Tôi thích tùy chọn này vì nó dễ dàng tạo ra N số ngẫu nhiên với -n. Ví dụ: Tạo 5 số ngẫu nhiên trong khoảng từ 1 đến 100 :shuf -i 1-100 -n 5
aerijman

Theo tôi hiểu những con số không phải là ngẫu nhiên. Nếu bạn chỉ định, shuf -i 1-10 -n 10bạn sẽ nhận được tất cả các số từ 1 đến 10 chính xác một. Nếu bạn chỉ định, -n 15bạn vẫn sẽ chỉ nhận được 10 số đó chính xác một lần. Đó thực sự chỉ là xáo trộn, không tạo ra số ngẫu nhiên.
radlan

Để có được số ngẫu nhiên với sự thay thế: -r
Geoffrey Anderson

36

Hãy thử điều này từ vỏ của bạn:

$ od -A n -t d -N 1 /dev/urandom

Ở đây, -t dxác định rằng định dạng đầu ra phải được ký thập phân; -N 1nói để đọc một byte từ /dev/urandom.


2
Câu hỏi yêu cầu số trong một phạm vi.
JSycamore

9
bạn có thể xóa dấu cách:od -A n -t d -N 1 /dev/urandom |tr -d ' '
Robert

23

bạn cũng có thể nhận được số ngẫu nhiên từ awk

awk 'BEGIN {
   # seed
   srand()
   for (i=1;i<=1000;i++){
     print int(1 + rand() * 100)
   }
}'

1
+1 bạn biết đấy, lúc đầu tôi mặc dù tại sao bạn lại muốn làm nó như thế này, nhưng thực sự tôi rất thích nó.
zelanix

1
Cảm ơn đã đưa ra một giải pháp bao gồm gieo hạt. Tôi không thể tìm thấy nó ở bất cứ đâu!
so.very.tired

2
+1 để gieo hạt. Điều đáng nói srand()là hạt giống của nó là thời gian CPU hiện tại. Nếu bạn cần phải xác định một hạt giống cụ thể, vì vậy RNG có thể được nhân đôi, sử dụng srand(x)ở đâu xlà hạt giống. Ngoài ra, được trích dẫn từ sổ tay chức năng số của GNU awk, "các triển khai awk khác nhau sử dụng các trình tạo số ngẫu nhiên khác nhau trong nội bộ." Kết quả cuối cùng là nếu bạn quan tâm đến việc tạo phân phối thống kê, bạn nên mong đợi các biến thể nhẹ đi từ thời gian chạy này sang thời gian tiếp theo trên nền tảng khác nhau (tất cả đang chạy awkhoặc gawk).
Cbhihe

18

Có $ RANDOM. Tôi không biết chính xác làm thế nào nó hoạt động. Nhưng nó đã có tác dụng. Để thử nghiệm, bạn có thể làm:

echo $RANDOM

17

Tôi thích thủ thuật này:

echo ${RANDOM:0:1} # random number between 1 and 9
echo ${RANDOM:0:2} # random number between 1 and 99

...


6
$ RANDOM nằm trong phạm vi 0 - 32767. Bạn sẽ kết thúc với nhiều số bắt đầu bằng 1, 2 hoặc 3, hơn là bạn sẽ 4-9. Nếu bạn ổn với phân phối không cân bằng, điều này sẽ hoạt động tốt.
jbo5112

2
@ jbo5112 bạn hoàn toàn đúng, còn hiển thị chữ số cuối thì sao? echo $ {RANDOM: 0-1} cho một chữ số, $ {RANDOM: 0-2} cho hai chữ số ...?
fraff

3
Nếu bạn sử dụng (các) chữ số cuối cùng, nó sẽ khá tốt, nhưng nó sẽ bao gồm 0 và 00. Trên một chữ số, 0-7 sẽ xảy ra 0,03% thường xuyên hơn 8-9. Trên 2 chữ số, 0-67 sẽ xảy ra thường xuyên hơn 0,3% so với 68-99. Nếu bạn cần phân phối số ngẫu nhiên tốt, hy vọng bạn không sử dụng bash. Với bản gốc: ${RANDOM:0:1}có 67,8% cơ hội cho bạn 1 hoặc 2, ${RANDOM:0:2}chỉ có 0,03% cơ hội cho bạn một số có một chữ số (nên là 1%) và cả hai đều có 0,003% cơ hội cho bạn 0 Vẫn có những trường hợp sử dụng trong trường hợp này là tốt (ví dụ: đầu vào không nhất quán).
jbo5112

12

Số ngẫu nhiên từ 0 đến 9 đã bao gồm.

echo $((RANDOM%10))

2
Xấu của tôi, đã không đọc trang người đàn ông đúng. $RANDOMchỉ đi từ 0 đến 32767. Đáng lẽ nên nói "Số ngẫu nhiên chủ yếu nằm trong khoảng từ 1 đến 3, với một vài người chạy cánh";)
David Newcomb

1
Gì? Nó vẫn sẽ nằm trong khoảng từ 0 đến 9, mặc dù 8 và 9 sẽ có ít khả năng xảy ra hơn 0 đến 7, như đã đề cập trong câu trả lời khác.
kini

6

Nếu bạn đang sử dụng hệ thống linux, bạn có thể nhận được một số ngẫu nhiên trong số / dev / ngẫu nhiên hoặc / dev / urandom. Hãy cẩn thận / dev / ngẫu nhiên sẽ chặn nếu không có đủ số ngẫu nhiên có sẵn. Nếu bạn cần tốc độ trên ngẫu nhiên, hãy sử dụng / dev / urandom.

Các "tệp" này sẽ được lấp đầy với các số ngẫu nhiên được tạo bởi hệ điều hành. Nó phụ thuộc vào việc triển khai / dev / ngẫu nhiên trên hệ thống của bạn nếu bạn nhận được các số ngẫu nhiên đúng hoặc giả. Các số ngẫu nhiên thực sự được tạo ra với sự trợ giúp hình thành tiếng ồn được thu thập từ trình điều khiển thiết bị như chuột, ổ cứng, mạng.

Bạn có thể nhận được số ngẫu nhiên từ tệp với dd


5

Tôi đã lấy một vài trong số những ý tưởng này và thực hiện một chức năng sẽ thực hiện nhanh chóng nếu cần nhiều số ngẫu nhiên.

Gọi odlà đắt tiền nếu bạn cần nhiều số ngẫu nhiên. Thay vào đó tôi gọi nó một lần và lưu trữ 1024 số ngẫu nhiên từ / dev / urandom. Khi nàorand được gọi, số ngẫu nhiên cuối cùng được trả về và chia tỷ lệ. Sau đó nó được xóa khỏi bộ nhớ cache. Khi bộ đệm trống, 1024 số ngẫu nhiên khác được đọc.

Thí dụ:

rand 10; echo $RET

Trả về một số ngẫu nhiên trong RET trong khoảng từ 0 đến 9.

declare -ia RANDCACHE
declare -i RET RAWRAND=$(( (1<<32)-1 ))

function rand(){  # pick a random number from 0 to N-1. Max N is 2^32
  local -i N=$1
  [[ ${#RANDCACHE[*]} -eq 0 ]] && { RANDCACHE=( $(od -An -tu4 -N1024 /dev/urandom) ); }  # refill cache
  RET=$(( (RANDCACHE[-1]*N+1)/RAWRAND ))  # pull last random number and scale
  unset RANDCACHE[${#RANDCACHE[*]}-1]     # pop read random number
};

# test by generating a lot of random numbers, then effectively place them in bins and count how many are in each bin.

declare -i c; declare -ia BIN

for (( c=0; c<100000; c++ )); do
  rand 10
  BIN[RET]+=1  # add to bin to check distribution
done

for (( c=0; c<10; c++ )); do
  printf "%d %d\n" $c ${BIN[c]} 
done

CẬP NHẬT: Điều đó không hoạt động tốt cho tất cả N. Nó cũng lãng phí các bit ngẫu nhiên nếu được sử dụng với N. nhỏ Lưu ý rằng (trong trường hợp này) một số ngẫu nhiên 32 bit có đủ entropy cho 9 số ngẫu nhiên trong khoảng từ 0 đến 9 (10 * 9 = 1.000.000.000 <= 2 * 32) chúng tôi có thể trích xuất nhiều số ngẫu nhiên từ mỗi giá trị nguồn ngẫu nhiên 32.

#!/bin/bash

declare -ia RCACHE

declare -i RET             # return value
declare -i ENT=2           # keep track of unused entropy as 2^(entropy)
declare -i RND=RANDOM%ENT  # a store for unused entropy - start with 1 bit

declare -i BYTES=4         # size of unsigned random bytes returned by od
declare -i BITS=8*BYTES    # size of random data returned by od in bits
declare -i CACHE=16        # number of random numbers to cache
declare -i MAX=2**BITS     # quantum of entropy per cached random number
declare -i c

function rand(){  # pick a random number from 0 to 2^BITS-1
  [[ ${#RCACHE[*]} -eq 0 ]] && { RCACHE=( $(od -An -tu$BYTES -N$CACHE /dev/urandom) ); }  # refill cache - could use /dev/random if CACHE is small
  RET=${RCACHE[-1]}              # pull last random number and scale
  unset RCACHE[${#RCACHE[*]}-1]  # pop read random number
};

function randBetween(){
  local -i N=$1
  [[ ENT -lt N ]] && {  # not enough entropy to supply ln(N)/ln(2) bits
    rand; RND=RET       # get more random bits
    ENT=MAX             # reset entropy
  }
  RET=RND%N  # random number to return
  RND=RND/N  # remaining randomness
  ENT=ENT/N  # remaining entropy
};

declare -ia BIN

for (( c=0; c<100000; c++ )); do
  randBetween 10
  BIN[RET]+=1
done

for c in ${BIN[*]}; do
  echo $c
done

Tôi đã thử điều này - mất 10 giây cpu 100% và sau đó in 10 số trông không ngẫu nhiên ở TẤT CẢ.
Carlo Wood

Tôi nhớ bây giờ. Mã này tạo ra 100.000 số ngẫu nhiên. Nó đặt mỗi cái trong một 'thùng' để xem mức độ ngẫu nhiên của nó. Có 10 thùng. Các số này phải giống nhau nếu mỗi số ngẫu nhiên trong khoảng từ 0 đến 9 có khả năng như nhau. Nếu bạn muốn in mỗi số, hãy lặp lại $ RET sau randB between 10.
philcolbourn

od -An -tu4 -N40 /dev/urandomsẽ tạo ra 10 số nguyên 32 bit không dấu ngẫu nhiên được phân tách bằng khoảng trắng. bạn có thể lưu trữ nó trong một mảng và sử dụng nó sau đó. mã của bạn dường như là một quá mức cần thiết.
Ali

@Ali, OP không xác định rằng họ muốn 32 bit cũng như bất kỳ số ngẫu nhiên có kích thước nào khác. Tôi và một số người khác giải thích câu hỏi này là cung cấp một số ngẫu nhiên trong một phạm vi. Hàm rand của tôi đạt được mục tiêu này và cũng giảm mất entropy, nếu cạn kiệt, khiến các chương trình bị chặn. od on / dev / urandom chỉ trả về 2 ^ N bit số ngẫu nhiên và OP sau đó sẽ cần lưu trữ nhiều giá trị vào một mảng, trích xuất tuần tự chúng từ mảng này và bổ sung mảng này. Có lẽ bạn có thể mã đây là một câu trả lời và xử lý các phạm vi số ngẫu nhiên khác?
philcolbourn

@philcolbourn, bạn đúng về OP không chỉ định loại số ngẫu nhiên anh ấy muốn và nó đã bỏ lỡ sự chú ý của tôi. Nhưng anh ta chỉ hỏi: "Làm thế nào để tạo một số ngẫu nhiên trong bash?". Quan điểm của tôi là anh ta chỉ yêu cầu một số ngẫu nhiên. Mặc dù nhà phê bình này cũng áp dụng cho nhận xét trước đây của tôi (tạo ra 10 số ngẫu nhiên).
Ali

5

Đọc từ các tập tin đặc biệt của nhân vật / dev / ngẫu nhiên hoặc / dev / urandom là cách tốt nhất.

Các thiết bị này trả về số thực sự ngẫu nhiên khi đọc và được thiết kế để giúp phần mềm ứng dụng chọn khóa bảo mật để mã hóa. Các số ngẫu nhiên như vậy được trích xuất từ ​​một nhóm entropy được đóng góp bởi các sự kiện ngẫu nhiên khác nhau. {LDD3, Jonathan Corbet, Alessandro Rubini và Greg Kroah-Hartman]

Hai tệp này là giao diện cho ngẫu nhiên kernel, đặc biệt

void get_random_bytes_arch(void* buf, int nbytes)

sẽ rút các byte thực sự ngẫu nhiên từ phần cứng nếu chức năng đó là do phần cứng được triển khai (thường là) hoặc nó rút ra từ nhóm entropy (bao gồm các khoảng thời gian giữa các sự kiện như gián đoạn chuột và bàn phím và các ngắt khác được đăng ký với SA_SAMPLE_RANDOM).

dd if=/dev/urandom count=4 bs=1 | od -t d

Điều này hoạt động, nhưng viết đầu ra không cần thiết từ ddthiết bị xuất chuẩn. Lệnh dưới đây chỉ cung cấp số nguyên tôi cần. Tôi thậm chí có thể nhận được số bit ngẫu nhiên được chỉ định khi tôi cần bằng cách điều chỉnh bitmask được cung cấp để mở rộng số học:

me@mymachine:~/$ x=$(head -c 1 /dev/urandom > tmp && hexdump 
                         -d tmp | head -n 1 | cut -c13-15) && echo $(( 10#$x & 127 ))

3

Thế còn:

perl -e 'print int rand 10, "\n"; '

Để có số ngẫu nhiên được bảo mật bằng mật mã, bạn cần đọc từ / dev / urandom hoặc sử dụng thư viện Crypt :: Random.
kh

3

Có lẽ tôi hơi muộn, nhưng còn việc sử dụng jotđể tạo một số ngẫu nhiên trong phạm vi trong Bash thì sao?

jot -r -p 3 1 0 1

Điều này tạo ra một số ngẫu nhiên ( -r) với độ chính xác 3 chữ số thập phân ( -p). Trong trường hợp cụ thể này, bạn sẽ nhận được một số từ 0 đến 1 ( 1 0 1). Bạn cũng có thể in dữ liệu tuần tự. Nguồn của số ngẫu nhiên, theo hướng dẫn, là:

Số ngẫu nhiên thu được thông qua arc4random (3) khi không có hạt giống nào được chỉ định và thông qua ngẫu nhiên (3) khi hạt giống được đưa ra.


Phải được cài đặt: sudo apt cài đặt athena-jot
xerostomus

3

Dựa trên các câu trả lời tuyệt vời của @Nelson, @Barun và @Robert, đây là tập lệnh Bash tạo ra các số ngẫu nhiên.

  • Có thể tạo bao nhiêu chữ số bạn muốn.
  • mỗi chữ số được tách biệt được tạo ra bởi /dev/urandomđó là tốt hơn nhiều so với Bash tích hợp trong$RANDOM
#!/usr/bin/env bash

digits=10

rand=$(od -A n -t d -N 2 /dev/urandom |tr -d ' ')
num=$((rand % 10))
while [ ${#num} -lt $digits ]; do
  rand=$(od -A n -t d -N 1 /dev/urandom |tr -d ' ')
  num="${num}$((rand % 10))"
done
echo $num

Chính xác những gì tôi đã sau.
Prometheus

2

Tạo số ngẫu nhiên trong phạm vi từ 0 đến n (số nguyên 16 bit đã ký). Kết quả được đặt trong biến $ RAND. Ví dụ:

#!/bin/bash

random()
{
    local range=${1:-1}

    RAND=`od -t uI -N 4 /dev/urandom | awk '{print $2}'`
    let "RAND=$RAND%($range+1)"
}

n=10
while [ $(( n -=1 )) -ge "0" ]; do
    random 500
    echo "$RAND"
done

1

Phân nhánh ngẫu nhiên của một chương trình hoặc có / không; 1/0; đầu ra đúng / sai:

if [ $RANDOM -gt 16383  ]; then              # 16383 = 32767/2 
    echo var=true/1/yes/go_hither
else 
    echo var=false/0/no/go_thither
fi

nếu bạn lười nhớ 16383:

if (( RANDOM % 2 )); then 
    echo "yes"
else 
    echo "no"
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.