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

srand#

זריעת מחולל המספרים הפסבדו־אקראיים.

srand מגדיר את המצב הפנימי של ה־PRNG ש־rand שואב ממנו. קראו לו עם שלם מפורש כדי לקבל זרם בר־שחזור, או קראו לו ללא ארגומנט כדי לזרוע מחדש ממקור האנטרופיה הטוב ביותר שהפלטפורמה מציעה. מאז Perl 5.14 srand מחזיר את הזרע שבו השתמש, כך שתוכלו לרשום אותו ולשחזר את אותה ריצה מאוחר יותר.

תקציר#

srand EXPR
srand

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

הזרע שהותקן, כשלם. עבור srand($n) ערך ההחזרה הוא $n (לאחר הקטימה הרגילה לשלם). עבור srand() ללא ארגומנט ערך ההחזרה הוא הזרע שנבחר אוטומטית שנשאב מאנטרופיית המערכת - לכדו אותו אם תרצו אי פעם לשחזר את הריצה.

my $seed = srand();                 # auto-seed, remember what we used
warn "seed for this run: $seed\n";

לפני Perl 5.14 srand לא החזיר דבר שימושי. קוד שמסתמך על החזרת הזרע צריך להכריז על הגרסה המינימלית:

use v5.14;                          # so srand returns the seed

מצב גלובלי#

srand משנה PRNG יחיד שמשתרע על כל התהליך. כל קריאה עוקבת ל־rand באותו תהליך - מכל חבילה, כל מודול, כל הקשר ללא־thread - קוראת מאותו מצב יחיד. אין RNG לכל־חבילה או לכל־תחום. השלכות:

  • קריאה ל־srand($k) בכל מקום בתוכנית משפיעה על כל קריאה מאוחרת ל־rand, כולל אלה בתוך מודולים שלא כתבתם.

  • שני תהליכים שקוראים ל־srand($k) עם אותו $k מפיקים זרמי rand זהים. זה הבסיס לבדיקות ברות־שחזור והסיבה שעליכם לזרוע מחדש לאחר fork.

  • אם srand לעולם אינו נקרא במפורש, הוא נקרא במשתמע ללא ארגומנט בפעם הראשונה ש־rand מופעל.

דוגמאות#

מקרה בדיקה בר־שחזור עם זרע מפורש - אותו קלט, אותו רצף, בכל ריצה:

srand(42);
my @sample = map { rand() } 1..5;   # identical on every invocation

זריעה מ־time() לאקראיות בעלת התנגשות נדירה על פני ריצות נפרדות שהחלו בשניות wall-clock שונות. זה הדפוס הקלאסי; הוא אינו ברמה קריפטוגרפית:

srand(time() ^ $$);                 # time XOR pid, old-school spread

דפוס שמירת־זרע, שחזור־זרע לבדיקה שבוחרת תת־קבוצה אקראית מהמקרים שלה אך רושמת מספיק כדי לשחזר כל כישלון:

my $seed = srand();                 # auto-seed, capture it
eval { run_randomised_tests() };
if ($@) {
    warn "FAILED with seed=$seed - reproduce with: srand($seed)\n";
    die $@;
}

זריעה מחדש לאחר fork כדי שההורה והילד לא ישתפו זרם:

my $pid = fork();
die "fork: $!" unless defined $pid;
if ($pid == 0) {
    srand();                        # child gets fresh entropy
    exec_child_work();
}

שני זרמים בלתי תלויים מאותו זרע - שימושי כשרוצים נקודת בדיקה וחידוש, או diff של שתי ריצות שהתפצלו לאחר נקודת החלטה:

srand(12345);
my @first  = map { rand() } 1..10;

srand(12345);
my @second = map { rand() } 1..10;  # identical to @first

מקרי קצה#

  • צורה ללא־ארגומנט זורעת אוטומטית עם האנטרופיה הטובה ביותר הזמינה. ב־Linux זה שואב מ־/dev/urandom (או getrandom(2)). הזרע רחב מספיק כך שיש להתייחס אליו כאל אטום, לא כשלם קטן.

  • אל תקראו ל־srand() ללא ארגומנטים יותר מפעם אחת לתהליך. המצב הפנימי של ה־PRNG כבר נושא יותר אנטרופיה מכל זרע יחיד יכול לשחזר, אז זריעה מחדש מאבדת אקראיות במקום להוסיף אותה. שתי הסיבות הלגיטימיות לקרוא לו שוב ושוב הן (א) עם זרע מפורש לשחזור, ו(ב) פעם אחת בכל ילד לאחר fork.

  • ארגומנטים עשרוניים נקטמים בשקט לשלם. srand(42) ו־srand(42.9) מתקינים את אותו זרע. תמיד העבירו שלם כדי שהכוונה תהיה ברורה.

  • אותו זרע ⇒ זרם rand זהה. זה כל הפואנטה לבדיקות; זוהי כל הבעיה לאבטחה.

  • לא בטוח קריפטוגרפית. rand/srand משתמשים ב־PRNG מהיר לא־קריפטוגרפי. אל תשתמשו בהם ליצירת טוקני session, nonces, מפתחות, saltים של סיסמאות, או כל דבר שיריב לא יוכל לחזות. השתמשו ב־[Crypt::URandom][cu] או קראו מ־/dev/urandom ישירות.

  • משתנה הסביבה PERL_RAND_SEED. אם מוגדר לשלם אי־שלילי בעת אתחול התהליך, srand() ללא ארגומנט (כולל הקריאה המשתמעת שמופעלת על ידי ה־rand הראשון) מתקין זרע דטרמיניסטי שנגזר מאותו ערך. הגזירה אינה מוגדרת בכוונה; רק שחזור אותו־ערך־אותו־בינארי־אותו־קוד מובטח. מיועד לדיבוג וניתוח ביצועים. המשתנה נקרא פעם אחת באתחול; שינוי %ENV מאוחר יותר אין לו השפעה על התהליך הנוכחי.

  • Threads (use threads): כל שיבוט מפרש מקבל מצב PRNG משלו. זריעה ב־thread אחד אינה משפיעה על אחר.

  • תחום הזרע: אין דרך לבצע local על מצב ה־PRNG. אם ספרייה שאתם קוראים לה ביניים מבצעת srand, הזרם שלכם מופרע. לכדו ושחזרו ידנית אם זה משנה.

הבדלים מהמקור#

תאימות מלאה עם Perl 5.42 המקורי. PERL_RAND_SEED מכובד עם אותו חוזה ״לא מוגדר בכוונה אך בר־שחזור״ כמו perl5.

ראו גם#

  • rand - צרכן המצב ש־srand מתקין; שואב את הערך הבא מהזרם הזרוע

  • time - שניות מאז ה־epoch; מקור הזרע הקלאסי מהיר־ומלוכלך לקוד שאינו אבטחתי (srand(time() ^ $$))

  • [Crypt::URandom][cu] - בתים אקראיים בטוחים קריפטוגרפית ממאגר האנטרופיה של מערכת ההפעלה; השתמשו בו, לא ב־rand, לטוקנים ומפתחות

  • [Math::Random::MT][mt] - PRNG של Mersenne Twister עם מצב בלתי תלוי משלו, כשאתם זקוקים לזרם שני שאינו מופרע על ידי קריאות ספרייה ל־rand

  • perlrun - מתעד את משתנה הסביבה PERL_RAND_SEED וכפתורי אתחול אחרים