วิธีรันคำสั่ง 1 จาก N ครั้งใน Bash


15

ฉันต้องการวิธีเรียกใช้คำสั่งแบบสุ่มพูด 1 จาก 10 ครั้ง มี builtin หรือ GNU coreutil ทำสิ่งนี้นึกคิด:

chance 10 && do_stuff

ที่do_stuffจะดำเนินการเพียง 1 ใน 10 ครั้ง? ฉันรู้ว่าฉันสามารถเขียนสคริปต์ได้ แต่ดูเหมือนว่าค่อนข้างง่ายและฉันสงสัยว่ามีวิธีที่กำหนดไว้หรือไม่


1
นี่เป็นตัวบ่งชี้ที่ดีพอสมควรว่าสคริปต์ของคุณอาจไม่เหมาะสมสำหรับการทุบตีเพื่อเป็นตัวเลือกที่เหมาะสม คุณควรพิจารณาภาษาการเขียนโปรแกรมที่สมบูรณ์มากขึ้นบางทีภาษาสคริปต์เช่น Python หรือ Ruby
Alexander - Reinstate Monica

@Alexander นี่ไม่ใช่สคริปต์จริงๆแม้แต่แค่บรรทัดเดียว ฉันใช้มันในงาน cron เพื่อแจ้งให้ฉันทราบแบบสุ่มทุก ๆ ครั้งและเพื่อเป็นการเตือนให้ทำบางสิ่งบางอย่างอีกครั้ง
retnikt

คำตอบ:


40

ในksh, Bash, Zsh, Yash หรือ BusyBox sh:

[ "$RANDOM" -lt 3277 ] && do_stuff

RANDOMตัวแปรพิเศษของ Korn, ทุบตี Yash, Z และ BusyBox หอยผลิตค่าจำนวนเต็มทศนิยมสุ่มหลอกระหว่าง 0 และ 32767 ทุกครั้งที่มีการประเมินเพื่อให้ด้านบน (ใกล้) หนึ่งในสิบโอกาส

คุณสามารถใช้ฟังก์ชันนี้เพื่อสร้างฟังก์ชั่นที่ทำงานตามที่อธิบายไว้ในคำถามของคุณอย่างน้อยใน Bash:

function chance {
  [[ -z $1 || $1 -le 0 ]] && return 1
  [[ $RANDOM -lt $((32767 / $1 + 1)) ]]
}

ลืมที่จะให้การโต้แย้งหรือการให้ข้อโต้แย้งที่ไม่ถูกต้องจะผลิตเป็นผลมาจาก 1 ดังนั้นจะไม่chance && do_stuffdo_stuff

สิ่งนี้ใช้สูตรทั่วไปสำหรับการใช้“ 1 in n$RANDOMซึ่งก็คือ[[ $RANDOM -lt $((32767 / n + 1)) ]]ให้โอกาส (⎣32767 / n ⎦ + 1) ในโอกาส 32768 ค่าnที่ไม่ใช่ปัจจัยของ 32768 จะทำให้เกิดอคติเนื่องจากการแบ่งที่ไม่สม่ำเสมอในช่วงของค่าที่เป็นไปได้


(1/2) ในการเยี่ยมชมปัญหานี้อีกครั้ง: เป็นเรื่องง่ายที่ควรมีขีด จำกัด บนจำนวนชิ้นส่วนเป็นไปไม่ได้ที่จะมีชิ้นส่วน 40.000 ตัวอย่างเช่น จริงๆแล้วขีด จำกัด ที่แท้จริงนั้นค่อนข้างเล็ก ปัญหาคือว่าการแบ่งจำนวนเต็มเป็นเพียงการประมาณว่าแต่ละส่วนมีขนาดใหญ่เพียงใด จำเป็นต้องมีสองส่วนติดต่อกันส่งผลให้ความแตกต่างของการ จำกัด$((32767/parts+1))นั้นสูงกว่า 1 หรือเราเสี่ยงต่อการเพิ่มจำนวนชิ้นส่วนใน 1 ในขณะที่ผลลัพธ์ของการหาร (และดังนั้นการ จำกัด ) จะเหมือนกัน (ต่อ .. )
ไอแซค

(2/2) (ต่อ .. ) ซึ่งจะมีตัวเลขมากกว่าที่มีอยู่จริง สูตรสำหรับนั่นคือ(32767/n-32767/(n+1))>=1การแก้สำหรับ n ที่ให้ขีด จำกัด ที่ ~ 181.5 ในความเป็นจริงจำนวนชิ้นส่วนสามารถทำงานได้ถึง 194 โดยไม่มีปัญหา แต่ที่ 195 ส่วนผลที่ได้คือ 151 เท่ากับผล 194 ชิ้น นั่นคือไม่ลงรอยกันและควรหลีกเลี่ยง ในระยะสั้นขีด จำกัด บนสำหรับจำนวนชิ้นส่วน (n) ควรเป็น 194 คุณสามารถทำการทดสอบขีด จำกัด ได้:[[ -z $1 || $1 -le 1 || $1 -ge 194 ]] && return 1
Isaac

25

โซลูชันที่ไม่ได้มาตรฐาน:

[ $(date +%1N) == 1 ] && do_stuff

ตรวจสอบว่าตัวเลขหลักสุดท้ายของเวลาปัจจุบันเป็นนาโนวินาทีเป็น 1!


ที่น่ากลัว.
Eric Duminil

2
คุณต้องตรวจสอบให้แน่ใจว่าการโทร[ $(date +%1N) == 1 ] && do_stuffไม่ได้เกิดขึ้นเป็นระยะ ๆ มิฉะนั้นการสุ่มจะเสีย คิดว่าwhile true; do [ $(date +1%N) == 1 ] && sleep 1; doneเป็นตัวอย่างที่เป็นนามธรรม อย่างไรก็ตามความคิดของการเล่นกับ nanoseconds เป็นสิ่งที่ดีจริงๆฉันคิดว่าฉันจะใช้มันดังนั้น +1
XavierStuvw

3
@ XavierStuvw ฉันคิดว่าคุณมีจุดถ้ารหัสกำลังตรวจสอบวินาที แต่นาโนวินาที ควรปรากฏแบบสุ่มจริงๆ
Eric Duminil

9
@EricDuminil: น่าเสียดายที่: 1) นาฬิการะบบไม่มีข้อผูกมัดตามสัญญาที่จะให้ความละเอียดระดับนาโนวินาทีจริง (มันอาจจะใกล้เคียงที่สุดclock_getres()), 2) ตัวกำหนดตารางเวลาไม่ได้รับอนุญาตจากตัวอย่างเช่นเริ่มจับเวลาครั้งในขอบเขต 100 นาโนวินาที (ซึ่งจะแนะนำอคติแม้ว่านาฬิกาขวา) 3) วิธีที่ได้รับการสนับสนุนของการได้รับค่าสุ่ม (ปกติ) อ่านจากหรือตรวจสอบค่าของ/dev/urandom $RANDOM
เควิน

1
@ เควิน: ขอบคุณมากสำหรับความคิดเห็น
Eric Duminil

21

ทางเลือกอื่นในการใช้$RANDOMคือshufคำสั่ง:

[[ $(shuf -i 1-10 -n 1) == 1 ]] && do_stuff

จะทำงาน มีประโยชน์สำหรับการสุ่มเลือกบรรทัดจากไฟล์เช่น สำหรับรายการเพลง


1
ไม่ทราบคำสั่งนั้น ดีมาก!
Eisenknurr

12

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

[ $(( $RANDOM % 10 )) == 0 ] && echo "You win" || echo "You lose"

1
$RANDOM % 10จะมีอคติเว้นแต่$RANDOMว่าผลิตผลหลายอย่างจาก 10 ค่าที่แตกต่างกัน (ซึ่งโดยทั่วไปจะไม่เกิดขึ้นในคอมพิวเตอร์แบบไบนารี)
27432

1
@phuclv "$RANDOM" -lt $((32767 / n + 1))ใช่มันจะมีอคติเช่นเดียวกับ
Eisenknurr

ใช่ค่าไบอัสเหมือนกันคือ 0.100006104 แทน 0.1 สำหรับ 1-in-10 (เมื่อตรวจสอบกับ 0)
สตีเฟ่น Kitt

1

ฉันไม่แน่ใจว่าคุณต้องการสุ่มหรือเป็นช่วงเวลา ... สำหรับช่วงเวลา:

for i in `seq 1 10 100`; do echo $i;done
1
11
21
31
41
51
61
71
81
91

คุณสามารถผสมกับเคล็ดลับ "$ RANDOM" ด้านบนเพื่อสร้างสิ่งที่วุ่นวายมากขึ้นตัวอย่างเช่น:

สำหรับฉันในseq 1 1000 $RANDOM; ทำ echo $ i เสร็จแล้ว

HTH :-)

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