จะสร้างเลขสุ่มใน Bash ได้อย่างไร


คำตอบ:


283

$RANDOMใช้ มันมักจะมีประโยชน์เมื่อใช้ร่วมกับการคำนวณทางคณิตศาสตร์อย่างง่าย ตัวอย่างเช่นในการสร้างตัวเลขสุ่มระหว่าง 1 ถึง 10 (รวม)

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

เครื่องกำเนิดไฟฟ้าที่เกิดขึ้นจริงในฟังก์ชั่นvariables.c เวอร์ชันที่เก่ากว่าเป็นเครื่องกำเนิดไฟฟ้าเชิงเส้นอย่างง่าย เวอร์ชัน 4.0 ของการใช้เครื่องกำเนิดไฟฟ้าที่มีการอ้างอิงถึงกระดาษ 1985 ซึ่งสันนิษฐานว่าหมายความว่ามันเป็นแหล่งที่ดีของตัวเลขเทียมหลอก ฉันจะไม่ใช้มันสำหรับการจำลอง (และแน่นอนว่าไม่ใช่สำหรับ crypto) แต่มันอาจจะเพียงพอสำหรับงานเขียนสคริปต์ขั้นพื้นฐานbrand()bash

หากคุณกำลังทำบางสิ่งที่ต้องมีการสุ่มตัวเลขที่รุนแรงคุณสามารถใช้/dev/randomหรือ/dev/urandomหากพวกเขามีให้:

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

21
ระวังที่นี่ ในขณะที่สิ่งนี้ดีในการหยิกการคำนวณทางคณิตศาสตร์กับตัวเลขสุ่มอาจส่งผลกระทบอย่างมากต่อการสุ่มผลลัพธ์ของคุณ ในกรณีที่$RANDOM % 108 และ 9 สามารถวัดได้ (แม้ว่าจะเล็กน้อย) มีโอกาสน้อยกว่า 0-7 แม้ว่า$RANDOMจะเป็นแหล่งข้อมูลสุ่มที่มีประสิทธิภาพ
dimo414

3
@ dimo414 ฉันอยากรู้อยากเห็น "เล็กน้อย" คุณมีแหล่งที่ฉันสามารถหาข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้?
PascalVKooten

58
โดย moduloing การป้อนข้อมูลแบบสุ่มของคุณคุณจะ " pigeon-holing " ผลลัพธ์ เนื่องจาก$RANDOMช่วงของมันคือ0-32767ตัวเลข0- 7แมปไปยัง3277อินพุตที่แตกต่างกันที่เป็นไปได้ แต่8และ9สามารถสร้างได้3276หลายวิธีเท่านั้น (เพราะ32768และ32769เป็นไปไม่ได้) นี่เป็นปัญหาเล็กน้อยสำหรับแฮ็กด่วน แต่หมายความว่าผลลัพธ์จะไม่สุ่มอย่างสม่ำเสมอ ไลบรารีแบบสุ่มเช่นเดียวกับ Java Randomให้ฟังก์ชันที่จะส่งกลับหมายเลขสุ่มอย่างสม่ำเสมอในช่วงที่กำหนดแทนที่จะเพียงปรับเปลี่ยนจำนวนที่ไม่สามารถหารได้
dimo414

1
@JFSebastian จริงมาก - ปัญหากับ modulo คือมันสามารถทำลายความสม่ำเสมอของRNG ใด ๆไม่ใช่แค่ PRNG ที่ไม่ดี แต่ขอบคุณที่เรียกสิ่งนี้ออกมา
dimo414

14
สำหรับบริบทแล้วนกพิราบขั้นพื้นฐานสำหรับ% 10 หมายถึง 8 และ 9 นั้นมีแนวโน้มที่จะเกิดขึ้นน้อยกว่า. 0-7 ประมาณ 0.3% หากเชลล์สคริปต์ของคุณต้องการหมายเลขสุ่มที่มีความแม่นยำมากกว่านั้นหมายความว่าใช้กลไกที่ซับซ้อนและเหมาะสมกว่า
เนลสัน

70

โปรดดู$RANDOM:

$RANDOM เป็นฟังก์ชัน Bash ภายใน (ไม่ใช่ค่าคงที่) ที่ส่งคืนเลขจำนวนเต็มเทียมในช่วง 0 - 32767 ไม่ควรใช้เพื่อสร้างคีย์การเข้ารหัส


1
ไม่32767ได้มีความหมายพิเศษใด ๆ
Jin Kwon

14
@JinKwon 32767เป็น2^16 / 2 - 1ขีด จำกัด สูงสุดของจำนวนเต็ม 16 บิตที่ลงชื่อแล้ว
Jeffrey Martinez

@ JinKwon คุณช่วยอธิบายได้ไหมว่าทำไมคุณถึงไม่พูดออกมา2^15 - 1? มันเทียบเท่าดังนั้นฉันแค่อยากรู้ว่ามีบางบริบทที่ฉันหายไป?
Brett Holman

11
@BrettHolman ฉันคิดว่าเขาพยายามที่จะชี้ให้เห็นส่วน "ลงนาม" ของจำนวนเต็ม 16 บิตที่ลงนามแล้ว 2 ^ 16 ค่าแบ่งครึ่งสำหรับโพสต์ & int เชิงลบ
ดี้

คำตอบนี้ไม่ตอบคำถาม
เด็กเหลือขอ

48

นอกจากนี้คุณยังสามารถใช้shuf (มีให้ใน coreutils)

shuf -i 1-100000 -n 1

คุณจะส่งผ่าน vars เป็นช่วงสิ้นสุดได้อย่างไร ผมได้รับนี้:shuf -i 1-10 -n 1: syntax error in expression (error token is "1-10 -n 1")
ดาด tutbrus

1
เพิ่ม$varช่วงท้ายแทนดังนี้:var=100 && shuf -i 1-${var} -n 1
knipwim

2
-nฉันชอบตัวเลือกนี้เป็นเรื่องง่ายที่จะสร้างตัวเลขสุ่มที่มี N เช่นสร้างหมายเลขสุ่ม 5 หมายเลขระหว่าง 1 ถึง 100 :shuf -i 1-100 -n 5
aerijman

เท่าที่ฉันเข้าใจตัวเลขไม่ได้สุ่ม หากคุณระบุshuf -i 1-10 -n 10คุณจะได้รับหมายเลขทั้งหมดตั้งแต่ 1 ถึง 10 ที่แน่นอน หากคุณระบุว่า-n 15คุณจะได้รับเพียง 10 หมายเลขเท่านั้น นั่นคือการสับเท่านั้นไม่สร้างตัวเลขสุ่ม
radlan

ในการรับหมายเลขสุ่มด้วยการแทนที่: -r
Geoffrey Anderson

36

ลองสิ่งนี้จากเปลือกของคุณ:

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

ที่นี่-t dระบุว่ารูปแบบผลลัพธ์ควรเป็นเลขทศนิยม กล่าวว่าในการอ่านหนึ่งไบต์จาก-N 1/dev/urandom


2
คำถามจะถามหาตัวเลขในช่วง
JSycamore

9
คุณสามารถลบช่องว่าง:od -A n -t d -N 1 /dev/urandom |tr -d ' '
Robert

23

คุณสามารถรับหมายเลขสุ่มได้จาก awk

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

1
+1 ตอนแรกฉันก็รู้ว่าทำไมคุณถึงอยากทำแบบนี้ แต่จริง ๆ แล้วฉันชอบมันมาก
zelanix

1
ขอบคุณที่ให้วิธีแก้ปัญหาที่รวมการเพาะ ฉันไม่พบที่ใดก็ได้!
so.very.tired

2
+1 สำหรับการเพาะ มันอาจจะคุ้มค่าที่จะกล่าวถึงว่าsrand()เมล็ดของเวลา CPU ปัจจุบัน หากคุณต้องการระบุเมล็ดพันธุ์ที่เฉพาะเจาะจงเพื่อให้สามารถทำซ้ำ RNG ให้ใช้srand(x)ตำแหน่งที่xเป็นเมล็ด นอกจากนี้ยังอ้างถึงจากคู่มือฟังก์ชั่นตัวเลขของ GNU awk "การใช้งาน awk ที่แตกต่างกันนั้นใช้ตัวสร้างหมายเลขสุ่มที่แตกต่างกันภายใน" ผลที่สุดคือหากคุณสนใจที่จะสร้างการกระจายทางสถิติคุณควรคาดหวังความผันแปรเล็กน้อยจากรันไทม์หนึ่งไปยังแพลตฟอร์มถัดไปบนแพลตฟอร์มที่แตกต่างกัน (ทำงานทั้งหมดawkหรือgawk)
Cbhihe

18

มี $ RANDOM อยู่ ฉันไม่รู้ว่ามันทำงานอย่างไร แต่มันได้ผล สำหรับการทดสอบคุณสามารถทำได้:

echo $RANDOM

16

ฉันชอบเคล็ดลับนี้:

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

...


7
$ RANDOM อยู่ในช่วง 0 - 32767 คุณจะได้ตัวเลขที่เริ่มต้นด้วย 1, 2 หรือ 3 มากกว่าที่คุณจะ 4-9 หากคุณโอเคกับการกระจายแบบไม่สมดุลสิ่งนี้จะใช้ได้ดี
jbo5112

2
@ jbo5112 คุณพูดถูกต้องแล้วหลักสุดท้ายคืออะไร? echo $ {RANDOM: 0-1} สำหรับหนึ่งหลัก, $ {RANDOM: 0-2} สำหรับสองหลัก ... ?
fraff

4
หากคุณใช้ตัวเลขสุดท้ายมันจะค่อนข้างดี แต่มันจะมีค่า 0 และ 0 ในหลักเดียว 0-7 จะเกิดขึ้น 0.03% บ่อยกว่า 8-9 ใน 2 หลัก 0-67 จะเกิดขึ้น 0.3% บ่อยกว่า 68-99 หากคุณต้องการการแจกแจงตัวเลขแบบสุ่มที่ดีหวังว่าคุณจะไม่ใช้ bash ด้วยต้นฉบับ: ${RANDOM:0:1}มีโอกาส 67.8% ที่จะให้คุณ 1 หรือ 2 ${RANDOM:0:2}เพียงมีโอกาส 0.03% ที่จะให้คุณเป็นตัวเลขหลักเดียว (ควรเป็น 1%) และทั้งสองมีโอกาส 0.003% ที่จะให้คุณเป็น 0 ยังคงมีการใช้เคสที่ปรับได้ (เช่นอินพุตที่ไม่สอดคล้องกัน)
jbo5112

12

ตัวเลขสุ่มตั้งแต่ 0 ถึง 9

echo $((RANDOM%10))

2
ไม่ดีฉันไม่ได้อ่านหน้าคนอย่างถูกต้อง $RANDOMไปจาก 0 ถึง 32767 เท่านั้นมันควรจะพูดว่า "หมายเลขสุ่มส่วนใหญ่ระหว่าง 1 และ 3 กับ wingers ไม่กี่";)
David Newcomb

1
อะไร? มันจะยังคงอยู่ระหว่าง 0 ถึง 9 แม้ว่า 8 และ 9 จะมีโอกาสเกิดขึ้นน้อยกว่า 0 ถึง 7 เล็กน้อยดังที่กล่าวไว้ในคำตอบอื่น
kini

6

หากคุณใช้ระบบ linux คุณสามารถรับจำนวนสุ่มจาก/ dev / random หรือ / dev / urandom ระวังตัว / dev / สุ่มจะบล็อกหากว่ามีตัวเลขสุ่มไม่เพียงพอ หากคุณต้องการความเร็วมากกว่าการใช้ randomness / dev / urandom

"ไฟล์" เหล่านี้จะถูกเติมด้วยตัวเลขสุ่มที่สร้างโดยระบบปฏิบัติการ ขึ้นอยู่กับการใช้ / dev / random ในระบบของคุณหากคุณได้รับตัวเลขสุ่มจริงหรือหลอก ตัวเลขสุ่มที่แท้จริงจะถูกสร้างขึ้นพร้อมกับเสียงรบกวนในแบบฟอร์มความช่วยเหลือที่รวบรวมจากไดรเวอร์อุปกรณ์เช่นเมาส์ฮาร์ดไดรฟ์เครือข่าย

คุณสามารถรับตัวเลขสุ่มจากไฟล์ด้วยdd


5

ฉันใช้ความคิดเหล่านี้สองสามข้อและสร้างฟังก์ชันที่ควรทำงานได้อย่างรวดเร็วหากต้องการตัวเลขสุ่มจำนวนมาก

การโทรodมีราคาแพงหากคุณต้องการตัวเลขสุ่มจำนวนมาก แต่ฉันเรียกมันครั้งเดียวและเก็บ 1024 หมายเลขสุ่มจาก / dev / urandom เมื่อrandมีการเรียกจำนวนสุ่มสุดท้ายจะถูกส่งกลับและปรับขนาด มันจะถูกลบออกจากแคช เมื่อแคชว่างเปล่าจะมีการอ่านตัวเลขสุ่มอีก 1024 ตัว

ตัวอย่าง:

rand 10; echo $RET

ส่งคืนตัวเลขสุ่มใน RET ตั้งแต่ 0 ถึง 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

UPDATE: มันใช้ไม่ได้ผลกับ N. ทั้งหมดมันยังเปลืองบิตสุ่มหากใช้กับ N. เล็ก ๆ สังเกตว่า (ในกรณีนี้) ตัวเลขสุ่ม 32 บิตมีเอนโทรปีเพียงพอสำหรับ 9 ตัวเลขสุ่มระหว่าง 0 และ 9 (10 * 9 = 1,000,000,000 <= 2 * 32) เราสามารถดึงตัวเลขสุ่มหลายตัวจากค่าแหล่งสุ่ม 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

ฉันลองสิ่งนี้ - ใช้เวลา 10 วินาทีของซีพียู 100% จากนั้นพิมพ์ตัวเลข 10 หมายเลขที่ไม่ได้ดูแบบสุ่มเลย
Carlo Wood

ฉันจำได้แล้ว. รหัสนี้สร้าง 100,000 หมายเลขสุ่ม มันทำให้แต่ละคนใน 'ถังขยะ' เพื่อดูว่ามันเป็นแบบสุ่ม มี 10 ถังขยะ ตัวเลขเหล่านี้ควรใกล้เคียงกันหากตัวเลขสุ่มระหว่าง 0 ถึง 9 มีโอกาสเท่ากัน ถ้าคุณต้องการที่จะพิมพ์แต่ละหมายเลข echo $ RET หลังจาก randB ระหว่าง 10
philcolbourn

od -An -tu4 -N40 /dev/urandomจะสร้างจำนวนเต็ม 32 บิตที่ไม่ได้ลงชื่อแบบสุ่ม 10 รายการคั่นด้วยช่องว่าง คุณสามารถเก็บไว้ในอาร์เรย์และใช้งานได้ในภายหลัง รหัสของคุณดูเหมือนจะเกินความจริง
Ali

@Ali, OP ไม่ได้ระบุว่าพวกเขาต้องการ 32 บิตหรือหมายเลขสุ่มขนาดอื่น ๆ ฉันและคนอื่น ๆ ตีความคำถามนี้ว่าเป็นการให้ตัวเลขสุ่มภายในช่วง ฟังก์ชั่นแรนด์ของฉันบรรลุเป้าหมายนี้และยังลดการสูญเสียของเอนโทรปีที่ถ้าหมดจะทำให้โปรแกรมปิดกั้น od on / dev / urandom คืนค่าตัวเลขสุ่ม 2 ^ N บิตจากนั้น OP จะต้องเก็บค่าหลายค่าไว้ในอาร์เรย์โดยแยกออกจากอาร์เรย์นี้ตามลำดับและเติมอาร์เรย์นี้ใหม่ บางทีคุณสามารถใช้รหัสนี้เป็นคำตอบและจัดการกับช่วงตัวเลขสุ่มอื่น ๆ ได้หรือไม่
philcolbourn

@philcolbourn คุณถูกต้องเกี่ยวกับ OP ไม่ได้ระบุชนิดของหมายเลขสุ่มที่เขาต้องการและพลาดความสนใจของฉัน เพียง แต่เขาถามว่า: "วิธีการสร้างจำนวนสุ่มในทุบตี?" ประเด็นของฉันคือเขาขอหมายเลขสุ่มเพียงหนึ่งหมายเลข แม้ว่านักวิจารณ์คนนี้จะใช้กับความคิดเห็นก่อนหน้าของฉัน (สร้างตัวเลขสุ่ม10ตัว) เช่นกัน
Ali

5

การอ่านไฟล์พิเศษจาก / dev / random หรือ / dev / urandom อักขระเป็นวิธีที่จะไป

อุปกรณ์เหล่านี้จะส่งกลับตัวเลขสุ่มอย่างแท้จริงเมื่ออ่านและออกแบบมาเพื่อช่วยให้แอพพลิเคชั่นซอฟต์แวร์เลือกคีย์ความปลอดภัยสำหรับการเข้ารหัส ตัวเลขสุ่มดังกล่าวถูกดึงมาจากเอนโทรปีของสระว่ายน้ำซึ่งมีส่วนร่วมในเหตุการณ์สุ่มต่างๆ {LDD3, Jonathan Corbet, Alessandro Rubini และ Greg Kroah-Hartman]

ไฟล์ทั้งสองนี้เป็นส่วนต่อประสานกับการสุ่มเคอร์เนลโดยเฉพาะ

void get_random_bytes_arch(void* buf, int nbytes)

ซึ่งดึงสุ่มไบต์อย่างแท้จริงจากฮาร์ดแวร์ถ้าฟังก์ชั่นดังกล่าวเป็นการใช้ฮาร์ดแวร์ (ปกติคือ) หรือดึงจากเอนโทรปีพูล (ประกอบด้วยการกำหนดเวลาระหว่างเหตุการณ์เช่นเมาส์และคีย์บอร์ดอินเตอร์รัปต์และอินเตอร์รัปต์อื่นที่ลงทะเบียนกับ SA_SAMPLE_RANDOM)

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

ใช้งานได้ แต่เขียนเอาต์พุตที่ไม่จำเป็นจากddไปยัง stdout คำสั่งด้านล่างให้จำนวนเต็มที่ฉันต้องการ ฉันยังสามารถรับจำนวนบิตสุ่มที่ระบุตามที่ฉันต้องการโดยการปรับ bitmask ที่กำหนดให้กับการขยายเลขคณิต:

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

3

เกี่ยวกับ:

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

1
สำหรับหมายเลขสุ่มที่ปลอดภัยเข้ารหัสคุณต้องอ่านจาก / dev / urandom หรือใช้ไลบรารี Crypt :: Random
kh

3

บางทีฉันอาจจะสายไปหน่อย แต่สิ่งที่เกี่ยวกับการใช้jotเพื่อสร้างตัวเลขสุ่มภายในช่วงใน Bash?

jot -r -p 3 1 0 1

สิ่งนี้จะสร้างตัวเลขสุ่ม ( -r) ที่มีความแม่นยำทศนิยม 3 ตำแหน่ง ( -p) ในกรณีนี้คุณจะได้รับหนึ่งหมายเลขระหว่าง 0 ถึง 1 ( 1 0 1) คุณยังสามารถพิมพ์ข้อมูลตามลำดับ แหล่งที่มาของตัวเลขสุ่มตามคู่มือคือ:

ตัวเลขสุ่มจะได้รับผ่าน arc4random (3) เมื่อไม่มีการระบุเมล็ดและผ่านการสุ่ม (3) เมื่อมีการให้เมล็ด


1
ต้องติดตั้ง: sudo apt install athena-jot
xerostomus

3

จากคำตอบที่ยอดเยี่ยมของ @Nelson @Barun และ @Robert นี่คือสคริปต์ Bash ที่สร้างตัวเลขสุ่ม

  • สามารถสร้างจำนวนหลักที่คุณต้องการ
  • แต่ละหลักจะถูกสร้างแยกจากกัน/dev/urandomซึ่งดีกว่าในตัวของ Bash มาก$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

สิ่งที่ฉันเป็นหลังจากนั้น
โพร

2

สร้างตัวเลขสุ่มในช่วง 0 ถึง n (จำนวนเต็ม 16 บิตที่ลงนามแล้ว) ชุดผลลัพธ์เป็นตัวแปร $ RAND ตัวอย่างเช่น:

#!/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

การแยกโปรแกรมแบบสุ่มหรือใช่ / ไม่ใช่; 1/0; เอาต์พุตของจริง / เท็จ:

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

จากถ้าคุณขี้เกียจที่จะจำ 16383:

if (( RANDOM % 2 )); then 
    echo "yes"
else 
    echo "no"
fi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.