פונקציות מספריות

rand#

מחזיר מספר פסאודו־אקראי בנקודה צפה בטווח חצי־פתוח [0, EXPR).

rand שולף את הערך הבא מזרם המספרים הפסאודו־אקראיים של המפענח ומשנה את קנה המידה ל־0 <= n < EXPR. התוצאה היא מספר Perl רגיל (double), אף פעם לא מספר שלם בעצמה - עטפו עם int כאשר אתם רוצים מספר שלם. EXPR הוא אופציונלי וברירת המחדל היא 1, כך ש־rand חשוף מניב float ב־[0, 1).

תקציר#

rand EXPR
rand

מה מקבלים בחזרה#

float בדיוק כפול n עם 0 <= n < EXPR. הגבול העליון אינו כלול: rand(6) אף פעם אינו מחזיר 6, רק ערכים נמוכים ממש ממנו. לתוצאת מספר שלם, הרכיבו עם int:

my $die = int(rand(6)) + 1;       # 1..6

ההתפלגות היא (בקירוב) אחידה לאורך הטווח. rand הוא אופרטור סקלרי - הוא מחזיר ערך אחד לכל קריאה, ללא קשר להקשר.

מצב גלובלי שהוא נוגע בו#

rand צורך את מצב ה־PRNG הפנימי של המפענח, המשותף עם srand. שני כללים נובעים מכך:

  • Autoseeding: הקריאה הראשונה ל־rand בתוכנית קוראת אוטומטית ל־srand אם טרם עשיתם זאת. הזרע נבחר מאנטרופיה ברמת התהליך, ולכן תוכניות לא־מזורעות מפיקות זרם שונה בכל ריצה.

  • זרם משותף: כל קריאת rand עוקבת - בכל מקום בתוכנית, בכל מודול - שולפת מאותה סדרה. קריאה ל־srand($seed) עם זרע קבוע הופכת את הסדרה הזו לדטרמיניסטית וניתנת לשחזור, וזה מה שאתם רוצים ל־test fixtures ולדיבוג. קריאה ל־srand() ללא ארגומנט מזריעה מחדש מאנטרופיה.

אין בידוד לכל־thread או לכל־תחום. אם אתם זקוקים לזרם עצמאי לפיסת קוד אחת, אל תשתמשו ב־rand; פנו למודול מחולל ייעודי.

דוגמאות#

שליפה בסיסית ב־[0, 1):

my $u = rand();                   # e.g. 0.4173...

הטלת קוביה - הניב הקנוני int(rand(N)) + 1:

my $roll = int(rand(6)) + 1;      # 1..6

אלמנט אקראי של מערך:

my @colors = qw(red green blue yellow);
my $pick   = $colors[ rand @colors ];

rand @colors משתמש באורך המערך (הקשר סקלר נכפה על־ידי ה־prototype של rand), כך שזה עובד לכל גודל מערך.

זרם ניתן לשחזור - קבעו את הזרע למבחנים:

srand(42);
my @sample = map { rand() } 1..5; # same five numbers every run

ניב ערבוב בשורה אחת. קריא, אבל מוטה - sort משווה זוגות יותר מפעם אחת, ו־rand() חד־פעמי לכל השוואה אינו מייצר תמורה אחידה:

my @shuffled = sort { rand() <=> 0.5 } @list;   # biased; do not use

לערבוב הוגן, השתמשו ב־List::Util::shuffle:

use List::Util qw(shuffle);
my @shuffled = shuffle @list;

הדמיית מטבע משוקלל - שימו לב שהגבול העליון הלא־כולל אומר ש־rand() < 0.3 יורה בהסתברות בדיוק 0.3:

my $heads = rand() < 0.3 ? "heads" : "tails";

מקרי קצה#

  • EXPR הוא 0: מטופל כ־1, ולכן rand(0) מתנהג כמו rand(1). זוהי התנהגות מתועדת של ה־upstream והיא נשמרת כאן, אך זוהי מוזרות היסטורית - אל תסתמכו עליה בקוד חדש.

  • EXPR שלילי: התוצאה אינה מוגדרת בפועל - אתם עשויים לקבל 0, אתם עשויים לקבל float שלילי, וההתנהגות אינה ניידת בין גרסאות Perl. אם אתם רוצים ערך ב־[-N, 0), כתבו rand($n) - $n במפורש.

  • EXPR הוא undef: מומר למחרוזת ל־"", מומר מספרית ל־0, ולכן מתנהג כמו rand(0)rand(1). תחת use warnings תקבלו אזהרת uninitialized.

  • EXPR אינו מספרי: ההמרה הרגילה מסקלר למספר חלה. rand("3abc") הוא rand(3) עם אזהרה לא־מספרית.

  • אינו מאובטח קריפטוגרפית: rand משתמש ב־PRNG מהיר המתאים לסימולציה, ערבוב, ו־test fixtures. הוא אינו מתאים ל־session tokens, password salts, חומר מפתח, או כל ערך רגיש מבחינה אבטחתית. עבור אלה, קראו מ־/dev/urandom ישירות או השתמשו ב־Crypt::URandom / Crypt::PRNG.

  • התנהגות thread/fork: לאחר fork, הן ההורה והן הילד יורשים את אותו מצב PRNG ויפיקו את אותו זרם אלא אם כן אחד מהם קורא ל־srand() כדי להזריע מחדש. foot-gun שכיח בשרתי pre-forking - הזריעו בילד.

  • EXPR גדול: המחולל מפיק 48 ביטים של אקראיות לכל שליפה; עבור EXPR קרוב או מעל 2**48, הפלט אינו מתפלג עוד בעידון לאורך הטווח. עבור רוחבים קריפטוגרפיים אינכם צריכים להשתמש ב־rand בכל מקרה (ראו למעלה).

הבדלים מ־upstream#

תואם מלא ל־upstream Perl 5.42.

ראו גם#

  • srand - הזרעת ה־PRNG במפורש לשחזוריות, או הזרעה מחדש בילד מפוצל

  • int - קטיעת תוצאת rand לכיוון אפס כדי לקבל מספר שלם אחיד ב־[0, N)

  • List::Util::shuffle - ערבוב Fisher-Yates הוגן; העדיפו זאת על פני הניב sort { rand() <=> 0.5 }

  • Crypt::URandom - בתים אקראיים מאובטחים קריפטוגרפית ממקור האנטרופיה של מערכת ההפעלה; השתמשו בזה ל־tokens, salts וחומר מפתח

  • Crypt::PRNG - PRNG מאובטח קריפטוגרפית עם API עשיר כאשר אתם זקוקים ליותר מבתים גולמיים