สร้างตัวเลขสุ่มในช่วงที่กำหนด


84

หลังจาก googling ไปเล็กน้อยฉันไม่สามารถหาวิธีง่ายๆในการใช้คำสั่ง shell เพื่อสร้างตัวเลขจำนวนเต็มฐานสิบแบบสุ่มที่รวมอยู่ในช่วงเฉพาะซึ่งอยู่ระหว่างขั้นต่ำและสูงสุด

ฉันอ่านเกี่ยวกับ/dev/random, /dev/urandomและ$RANDOMแต่ไม่มีของเหล่านี้สามารถทำสิ่งที่ฉันต้องการ

มีคำสั่งที่มีประโยชน์หรือวิธีการใช้ข้อมูลก่อนหน้านี้หรือไม่?


2
ดูคำถามและคำตอบ SO นี้: stackoverflow.com/questions/8988824/ …รวมถึงstackoverflow.com/questions/2556190/นี้
slm


ที่เกี่ยวข้อง: unix.stackexchange.com/questions/124478/…
slm

คำตอบ:


45

ใน POSIX toolchest คุณสามารถใช้awk:

awk -v min=5 -v max=10 'BEGIN{srand(); print int(min+rand()*(max-min+1))}'

อย่าได้ใช้เป็นแหล่งที่มาเพื่อสร้างรหัสผ่านหรือข้อมูลที่เป็นความลับเช่นเป็นส่วนใหญ่awkการใช้งานจำนวนสามารถจะเดาได้ขึ้นอยู่กับเวลาที่สั่งกำลังวิ่ง

ด้วยawkการนำไปใช้หลายอย่างคำสั่งนั้นรันสองครั้งภายในวินาทีเดียวกันโดยทั่วไปจะให้ผลลัพธ์เดียวกัน


1
1 .. ตามที่ใช้ในสคริปต์เพื่อกำหนดส่งออกไปยังตัวแปร (w / o การแนะนำตัวละครขึ้นบรรทัดใหม่และใช้เป็นศูนย์ padding สำหรับสองสถานที่):x=$(awk -v min=$min_var -v max=$max_var 'BEGIN{srand(); printf("%.2d", int(min+rand()*(max-min+1)))}')
คริส

มันขึ้นอยู่กับเวลา? เนื่องจากฉันได้รับค่าเท่ากันในการวิ่งติดต่อกัน 2 ครั้ง ...
Shai Alon

@ShaiAlon เวลาปัจจุบันมักถูกใช้เป็นค่าเริ่มต้นสำหรับ srand () ดังนั้นโดยปกติใช่มันขึ้นอยู่กับเวลาดูประโยคของStéphaneเกี่ยวกับสิ่งนั้นในคำตอบของเขา awk -v min=5 -v max=10 -v seed="$(od -An -N4 -tu4 /dev/urandom)" 'BEGIN{srand(seed+0); print int(min+rand()*(max-min+1))}'คุณจะได้รับรอบว่าโดยการเปลี่ยนเมล็ดเริ่มต้นเป็นจำนวนสุ่มที่มี คุณสามารถใช้$RANDOMแทนod ... /dev/randomแต่แล้วค่าเมล็ดของคุณอยู่ในช่วงที่ค่อนข้างเล็กคือ 0 ถึง 32767 และดังนั้นคุณอาจได้รับการทำซ้ำที่เห็นได้ชัดเจนในผลลัพธ์ของคุณเมื่อเวลาผ่านไป
เอ็ดมอร์ตัน

141

คุณสามารถลองได้shufจาก coreutils ของ GNU:

shuf -i 1-100 -n 1

วิธีนี้ใช้ได้กับ Busybox shufด้วย
cov

ทำงานในราสเบียนและ GitBash ด้วย
nPcomp

30

ส่วนนิดหนึ่ง

บน BSD และ OSX คุณสามารถใช้jotเพื่อส่งกลับหมายเลขสุ่ม ( -r) เดียวจากช่วงเวลาminไปถึงmaxรวม

$ min=5
$ max=10
$ jot -r 1 $min $max

ปัญหาการกระจาย

น่าเสียดายที่ช่วงและการแจกแจงของตัวเลขที่สร้างแบบสุ่มนั้นได้รับอิทธิพลจากความจริงที่ว่า jot ใช้เลขคณิตทศนิยมที่มีความแม่นยำสองเท่าภายในและ printf (3) สำหรับรูปแบบเอาต์พุตซึ่งทำให้เกิดปัญหาการปัดเศษและการตัดปลาย ดังนั้นช่วงเวลาminและmaxถูกสร้างขึ้นน้อยลงตามที่แสดง:

$ jot -r 100000 5 10 | sort -n | uniq -c
9918  5
20176 6
20006 7
20083 8
19879 9
9938  10

ใน OS X 10.11 (El Capitan) สิ่งนี้ดูเหมือนจะได้รับการแก้ไขแล้ว:

$ jot -r 100000 5 10 | sort -n | uniq -c
16692 5
16550 6
16856 7
16579 8
16714 9
16609 10  

และ...

$ jot -r 1000000 1 10 | sort -n | uniq -c
100430 1
99965 2
99982 3
99796 4
100444 5
99853 6
99835 7
100397 8
99588 9
99710 10

การแก้ปัญหาการกระจาย

สำหรับ OS X เวอร์ชั่นเก่าโชคดีที่มีวิธีแก้ไขหลายวิธี หนึ่งคือการใช้การแปลงจำนวนเต็ม printf (3) max+1ข้อแม้เพียงว่าช่วงสูงสุดตอนนี้กลายเป็น ด้วยการใช้การจัดรูปแบบจำนวนเต็มเราจะได้รับการแจกแจงอย่างยุติธรรมตลอดช่วงเวลาทั้งหมด:

$ jot -w %i -r 100000 5 11 | sort -n | uniq -c
16756 5
16571 6
16744 7
16605 8
16683 9
16641 10

ทางออกที่สมบูรณ์แบบ

ในที่สุดเพื่อให้ได้ลูกเต๋าที่ยุติธรรมโดยใช้วิธีแก้ปัญหาเรามี:

$ min=5
$ max_plus1=11  # 10 + 1
$ jot -w %i -r 1 $min $max_plus1

การบ้านเสริม

ดูjot (1)สำหรับคณิตศาสตร์ที่เต็มไปด้วยเลือดและรายละเอียดการจัดรูปแบบและตัวอย่างอื่น ๆ อีกมากมาย


15

$RANDOMตัวแปรเป็นปกติไม่ได้เป็นวิธีที่ดีในการสร้างค่าสุ่มดี ผลลัพธ์ของ/dev/[u]randomความต้องการยังต้องถูกแปลงก่อน

วิธีที่ง่ายกว่าคือการใช้ภาษาระดับสูงกว่าเช่น python:

ในการสร้างตัวแปรจำนวนเต็มแบบสุ่มระหว่าง 5 ถึง 10 (5 <= N <= 10) ให้ใช้

python -c "import random; print random.randint(5,10)"

อย่าใช้สิ่งนี้สำหรับแอปพลิเคชันเข้ารหัสลับ


สิ่งนี้มีประโยชน์ ขอบคุณมาก :) ตามที่ลงชื่อบ่อยครั้งได้ทำงานกับ Unix-> Solaris 10 GNU utils ไม่ได้รวมอยู่ในค่าเริ่มต้น สิ่งนี้ใช้ได้ผล
propatience

11

คุณสามารถรับตัวเลขสุ่มผ่าน urandom

head -200 /dev/urandom | cksum

เอาท์พุท:

3310670062 52870

เพื่อดึงส่วนหนึ่งของหมายเลขข้างต้น

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

จากนั้นผลลัพธ์ก็คือ

3310670062


head -200(หรือเทียบเท่า POSIX head -n 100) ผลตอบแทน 200 ครั้งแรกที่สาย/dev/urandomของ /dev/urandomไม่ใช่ไฟล์ข้อความคำสั่งนั้นสามารถส่งคืนจาก 200 ไบต์ (ทั้งหมด 0x0a, LF ใน ASCII) ไปเป็นอินฟินิตี้ (ถ้า 0xa ไบต์เกิดขึ้นไม่เคยถูกส่งกลับ) แต่ค่าระหว่าง 45k และ 55k เป็นไปได้มากที่สุด cksum ส่งกลับจำนวน 32bit จึงไม่มีจุดหมายที่จะได้รับมากกว่า 4 /dev/urandomไบต์จาก
Stéphane Chazelas

7

ในการสร้างตัวแปรจำนวนเต็มแบบสุ่มระหว่าง 5 ถึง 10 (รวมทั้งคู่) ให้ใช้

echo $(( RANDOM % (10 - 5 + 1 ) + 5 ))

% ทำงานเป็นตัวดำเนินการแบบโมดูโล

อาจมีวิธีที่ดีกว่าในการแปลงตัวแปรสุ่ม$RANDOMเป็นช่วงเฉพาะ อย่าใช้สิ่งนี้สำหรับแอปพลิเคชันเข้ารหัสลับหรือในกรณีที่คุณต้องการตัวแปรสุ่มที่มีการแจกแจงเท่ากันจริง ๆ (เช่นสำหรับการจำลอง)


3
ในการใช้งานเชลล์จำนวนมากที่มี $ RANDOM (โดยเฉพาะที่เก่ากว่าโดยเฉพาะอย่างยิ่งทุบตี) ซึ่งไม่ได้สุ่มมากนัก ในเชลล์ส่วนใหญ่จำนวนอยู่ระหว่าง 0 ถึง 65535 ดังนั้นช่วงใด ๆ ที่ความกว้างไม่ใช่พลังของทั้งสองจะมีความคลาดเคลื่อนการแจกแจงความน่าจะเป็น (ในกรณีนี้หมายเลข 5 ถึง 8 จะมีความน่าจะเป็น 10923/65536 ขณะที่ 9 และ 10 จะมีความน่าจะเป็น 10922/65536) ช่วงที่กว้างขึ้นความคลาดเคลื่อนที่มากขึ้น
Stéphane Chazelas

ขออภัยนั่นคือ 32767 ไม่ใช่ 65535 ดังนั้นการคำนวณข้างต้นไม่ถูกต้อง (และที่แย่กว่านั้นจริงๆ)
Stéphane Chazelas

@ StéphaneChazelasฉันมีสิ่งนี้อยู่ในใจขณะที่ฉันเขียนว่ามีวิธีที่ดีกว่า ...
jofel

5

# echo $(( $RANDOM % 256 )) จะสร้างตัวเลข "สุ่ม" ระหว่าง 0-255 ในภาษาท้องถิ่น * sh


ดูเหมือนกับคำตอบอื่น ๆเมื่อ 2 ปีที่แล้ว
Jeff Schaller

1
ปัญหานี้ขึ้นอยู่กับปัญหาช่องทางแคบ (TL; DR: ผลลัพธ์บางรายการมีแนวโน้มมากกว่าปัญหาอื่น) ถ้าหากใช้ 256 คุณจะใช้ค่าที่ไม่แบ่ง 32768 เท่า ๆ กัน
toon81

4

อาจใช้UUID(บน Linux) เพื่อดึงหมายเลขสุ่ม

$ cat /proc/sys/kernel/random/uuid
cdd52826-327d-4355-9737-895f58ad11b4

เพื่อให้ได้ตัวเลขสุ่มระหว่าง70และ100

POSIXLY_CORRECT=1 awk -F - '{print(("0x"$1) % 30 + 70)}
   ' /proc/sys/kernel/random/uuid


1

หากต้องการอยู่ภายในการทุบตีและใช้ตัวแปร $ RANDOM แต่หลีกเลี่ยงการกระจายที่ไม่สม่ำเสมอ:

#!/bin/bash
range=10 
floor=20

if [ $range -gt 32768 ]; then
echo 'range outside of what $RANDOM can provide.' >&2
exit 1 # exit before entering infinite loop
fi 

max_RANDOM=$(( 2**15/$range*$range ))
r=$RANDOM
until [ $r -lt $max_RANDOM ]; do
r=$RANDOM
done
echo $(( r % $range + $floor ))

ตัวอย่างนี้จะให้ตัวเลขสุ่มตั้งแต่ 20 ถึง 29


$rควรเริ่มต้นที่ 65535 หรือค่าสูงอื่น ๆ เพื่อหลีกเลี่ยง[: -lt: unary operator expectedข้อผิดพลาด
LawrenceC

แก้ไขการเริ่มต้น $ r ก่อนการทดสอบลูป ขอบคุณ @ LawrenceC!
Than Angell

0

การกระจายที่ดี:

สำหรับ ((i = 1; i <= 100000; i ++)) ทำ echo $ ((RANDOM% (20 - 10 + 1) + 10)); เสร็จสิ้น เรียง -n | uniq -c

นับค่า

9183 10

9109 11

8915 12

9037 13

9100 14

9138 15

9125 16

9261 17

9088 18

8996 19

9048 20

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.