عنوان:

‫تاریخچه تحول مکانیزم‌های تولید اعداد تصادفی تا دات‌نت ۱۱


نویسنده: وحید نصیری
تاریخ: ۱۴۰۵/۰۴/۰۹ ۰۹:۱۵
آدرس: www.dntips.ir
تولید اعداد تصادفی در دات‌نت (.NET) تاریخچه پرفراز و نشیبی داشته است. کلاس System.Random که از اولین نسخه‌های دات‌نت وجود داشته، به مرور زمان دستخوش تغییرات ساختاری بزرگی شده تا مشکلاتی مانند عدم امنیت در محیط‌های چندنخی (Thread Safety)، سوءاستفاده‌های امنیتی و محدودیت‌های نوع داده (Data Type) را برطرف کند. در ادامه تاریخچه تکاملی مکانیزم‌های تولید اعداد تصادفی در نگارش‌های مختلف دات‌نت را بررسی می‌کنیم:

۱. دات‌نت فریم‌ورک ۱.۰ تا ۴.۸ (.NET Framework) - دوران کلاسیک و چالش‌ها
در نسخه‌های قدیمی، کلاس اصلی برای تولید اعداد تصادفی System.Random بود. این کلاس با الگوریتم شبه‌تصادفی (PRNG) مبتنی بر الگوریتم تفاضلی دونالد کنوت (Donald Knuth) کار می‌کرد.
- نحوه کار: به صورت پیش‌فرض از زمان سیستم (System Time) به عنوان Seed استفاده می‌کرد.
- بزرگ‌ترین چالش‌ها:
  • عدم امنیت در چندنخی (Thread Safety): اگر دو نخ (Thread) همزمان متد Next را روی یک نمونه مشترک از Random صدا می‌زدند، وضعیت داخلی کلاس خراب می‌شد و در بسیاری از مواقع، پس از آن به طور مداوم عدد 0 را برمی‌گرداند.
  • تکراری بودن نمونه‌های همزمان: اگر چند نمونه از Random در یک زمان بسیار کوتاه (مثلاً درون یک حلقه) ساخته می‌شدند، به دلیل Seed یکسان (زمان سیستم)، همگی اعداد کاملاً مشابهی تولید می‌کردند.
- محدودیت متدها: متدهای موجود فقط به Next() (برای intNextDouble() و NextBytes() محدود می‌شدند. برای تولید انواع دیگر مانند byte یا long باید فرمول‌های ریاضی دستی نوشته می‌شد.
برای حل مشکل امنیت، توسعه‌دهندگان مجبور بودند از کلاس RNGCryptoServiceProvider استفاده کنند که کندتر بود اما امنیت بالایی داشت.

۲. دات‌نت کور ۲.۱ تا ۳.۱ (.NET Core) - بهبود Seed اولیه
مایکروسافت برای حل مشکل تکراری شدن اعداد در نمونه‌سازی‌های همزمان، نحوه تولید Seed پیش‌فرض را تغییر داد.
  • تغییر کلیدی: به جای اتکای صرف به زمان سیستم، الگوریتم بهینه‌سازی شد تا در صورت ساخته شدن همزمان چندین شیء Random، هرکدام Seed متفاوتی دریافت کنند و اعداد متفاوتی تولید کنند. با این حال، مشکل Thread-Safety همچنان پابرجا بود.

۳. دات‌نت ۵ (.NET 5) - تغییر الگوریتم زیرین
در دات‌نت ۵، الگوریتم زیرین کلاس System.Random از الگوریتم قدیمی Knuth به الگوریتم مدرن‌تر و سریع‌تر xoshiro256 (یا xoroshiro128 برای سیستم‌های ۳۲ بیتی) تغییر یافت. این تغییر باعث شد عملکرد (Performance) تولید اعداد تصادفی به شدت بهبود یابد و کیفیت توزیع آماری اعداد تصادفی نیز بهتر شود.

۴. دات‌نت ۶ (.NET 6) - انقلاب باRandom.Shared
دات‌نت ۶ یکی از بزرگ‌ترین و مهم‌ترین تغییرات را در این زمینه ایجاد کرد و آن معرفی خصوصیت Random.Shared بود.
  • حل مشکل Thread Safety: این ویژگی یک نمونه Thread-Safe و ایستا (Static) از کلاس Random را در اختیار شما می‌گذارد. دیگر نیازی به ساختن نمونه‌های جدید با کلمه کلیدی new نبود و نیازی هم به قفل‌گذاری (Locking) در محیط‌های چندنخی وجود نداشت.
  • معرفی متدهای جدید: متدهای جدیدی مانند GetItems و Shuffle در نسخه‌های بعدی بر پایه این ساختار شکل گرفتند.

۵. دات‌نت ۷ و ۸ (.NET 7 & 8) - پشتیبانی از داده‌های بزرگ‌تر و متدهای کاربردی
در دات‌نت ۷ و ۸، مایکروسافت متدهای عمومی کلاس Random را گسترش داد:
  • پشتیبانی از انواع اعشاری دیگر: متدهای NextSingle() (برای نوع float) در دات‌نت ۵ معرفی شده بودند، اما در دات‌نت ۷ الگوهای کاربردی‌تری برای تولید توابع توزیع آماری اضافه شد.
  • متد Shuffle و GetItems: در دات‌نت ۸ متد Shuffle برای برهم زدن تصادفی آرایه‌ها/لیست‌ها و متد GetItems برای انتخاب تصادفی چند آیتم از یک مجموعه معرفی شدند که کارهای رایج توسعه‌دهندگان را بسیار آسان کرد.
با این حال، برای گرفتن اعداد صحیح غیر از int (مانند long یا byte)، توسعه‌دهندگان هنوز باید دست به دامن ترفندهایی مانند متد NextBytes یا تبدیل‌ نوع‌های دستی (مانند همان مثالی که در متن خود آوردید) می‌شدند.

۶. دات‌نت ۱۱ (.NET 11) - بومی‌سازی انواع داده صحیح باNextInteger
در پیش‌نمایش دات‌نت ۱۱، مایکروسافت با استفاده از ویژگی‌های مدرن سی‌شارپ (مانند Generic Math که در دات‌نت ۷ معرفی شد)، یکی از قدیمی‌ترین نقص‌های طراحی System.Random را برطرف کرده است.
  • قبل از دات‌نت ۱۱: متد Next() فقط خروجی int (علامت‌دار ۳۲ بیتی) می‌داد. برای گرفتن یک byte یا short تصادفی، باید سقف محدوده را جابجا می‌کردید و نوع خروجی را به صورت صریح (Explicit Cast) تغییر می‌دادید. این کار کدهای ناخوانا تولید می‌کرد و احتمال خطای خارج از محدوده (Overflow) وجود داشت.
  • در دات‌نت ۱۱: با معرفی متد جنریک NextInteger، اکنون تمام انواع داده صحیح (Integral Types) از جمله byte, sbyte, short, ushort, int, uint, long, ulong, nint, nuint به صورت بومی و بدون نیاز به کست کردن پشتیبانی می‌شوند. همچنین این متدها اورلودهایی برای تعیین محدوده (Range) دارند که امنیت کد را به شدت بالا می‌برد:

var randomByte = Random.Shared.NextInteger<byte>();

Console.WriteLine(randomByte);

خلاصه سیر تحول:
  • دات‌نت کلاسیک: ناامن در چندنخی، تکراری شدن اعداد در حلقه‌ها، محدود به int.
  • دات‌نت ۵: تغییر الگوریتم زیرین به xoshiro برای سرعت بیشتر.
  • دات‌نت ۶: حل دائمی مشکل چندنخی با معرفی Random.Shared.
  • دات‌نت ۸: اضافه شدن ابزارهای جانبی مانند Shuffle و GetItems.
  • دات‌نت ۱۱: حل محدودیت نوع داده با معرفی متد جنریک NextInteger برای پوشش تمام انواع داده‌های صحیح.