عشوائي لكن قابل للتكرار
تحتاج محاكاة مونت كارلو والبوتستراب واختبارات كثيرة إلى أعداد عشوائية، لكن نتيجة لا يستطيع أحد إعادة إنتاجها لا قيمة لها: لا يمكن تصحيحها ولا مقارنة نسختين من نموذج ولا عرضها على زميل. والأعداد التي يولّدها الحاسوب ليست عشوائية حقًّا. مولّد الأعداد شبه العشوائية خوارزمية حتمية تنتج، انطلاقًا من قيمة أولية تسمى البذرة (seed)، متتالية طويلة تتصرف كسحوبات عشوائية. البذرة نفسها تعطي المتتالية نفسها دائمًا وعلى كل جهاز.
import numpy as np
rng = np.random.default_rng(42) # a Generator created from the seed 42
print(rng.random(3).round(4).tolist()) # [0.774, 0.4389, 0.8586]
rng = np.random.default_rng(42) # the same seed again: the same numbers
print(rng.random(3).round(4).tolist()) # [0.774, 0.4389, 0.8586]
rng = np.random.default_rng(43) # another seed: other numbers
print(rng.random(3).round(4).tolist()) # [0.6523, 0.0438, 0.02]استخدم Generator لا حالة عامة
تستدعي الشيفرة القديمة np.random.seed(42) مرة واحدة ثم np.random.normal(...) في أي مكان: كل الشيفرة تتشارك مولّدًا عامًّا خفيًّا واحدًا. فأي مكتبة أو أي سطر جديد يسحب عددًا يزيح متتالية الجميع، فتتغير النتائج لأسباب لا علاقة لها بنموذجك. والطريقة الحديثة إنشاء كائن Generator بـ np.random.default_rng(seed) وتمريره صراحةً. وتتولى دوالّه random وnormal وchoice وintegers كل السحب، ولا يتداخل مولّدان أبدًا.
def simulate_returns(seed, n, mu, sigma):
rng = np.random.default_rng(seed) # a private generator, not the global one
return rng.normal(mu, sigma, size=n)
first = simulate_returns(7, 5, 0.0005, 0.01)
second = simulate_returns(7, 5, 0.0005, 0.01)
print(first.round(5).tolist()) # [0.00051, 0.00349, -0.00224, -0.00841, -0.00405]
print(np.array_equal(first, second)) # True repeatableإعادة السحب والتيارات المستقلة
يعيد البوتستراب سحب عيّنة من التاريخ لمعرفة مقدار ما كان يمكن أن يتغير الإحصاء: اسحب من العوائد بقدر ما لديك مع الإرجاع بـ rng.choice(returns, size=len(returns), replace=True). تظهر بعض الأيام مرتين وبعضها لا يظهر. وحين تعمل محاكاة كثيرة بالتوازي يكون إعطاء كل عملية البذور 1 و2 و3 محفوفًا بالمخاطر لأن البذور المتجاورة غير مضمونة الاستقلال. يشتق np.random.SeedSequence(seed).spawn(k) عدد k من المتتاليات الفرعية المصممة كي لا تتداخل؛ ابنِ default_rng(child) من كل منها.
data = np.array([1, 2, 3, 4, 5])
rng = np.random.default_rng(1)
print(rng.choice(data, size=len(data), replace=True).tolist()) # [3, 3, 4, 5, 1] 3 appears twice, 2 not at all
# Three independent streams from one seed, for three parallel simulations
children = np.random.SeedSequence(2026).spawn(3)
streams = [np.random.default_rng(child) for child in children]
print([stream.random(2).round(4).tolist() for stream in streams])
# [[0.3827, 0.0204], [0.6711, 0.158], [0.5508, 0.2339]]rng = np.random.default_rng(42)
print(rng.integers(1, 7, size=8).tolist()) # [1, 5, 4, 3, 3, 6, 1, 5] eight dice rolls, repeatable
rng = np.random.default_rng(42)
print(rng.uniform(-0.01, 0.01, 3).round(5).tolist()) # [0.00548, -0.00122, 0.00717]rng = np.random.default_rng(seed): البذرة نفسها تعطي الأعداد نفسها دائمًا؛ استخدم دوالّهrandomوnormalوchoiceوintegers.- مرّر المولّد بدل استخدام الحالة العامة الخفية
np.random.seed. choice(..., replace=True)هو بوتستراب؛ وSeedSequence(seed).spawn(k)يعطيkتيارًا مستقلًا؛ سجّل البذرة المستخدمة.
التالي: السحب من التوزيعات، أي تحويل أعداد عشوائية منتظمة إلى سحوبات طبيعية وأسية ومتقطعة.
المختبر: ما الذي يُقيَّم
- أنشئ مولّدًا قابلًا للتكرار باستخدام
make_rng25 نقطة - ولّد عوائد قابلة للتكرار باستخدام
simulate_returns25 نقطة - أعد سحب عيّنة من التاريخ باستخدام
bootstrap_returns25 نقطة - قسّم بذرة واحدة إلى تيارات مستقلة باستخدام
independent_streams25 نقطة