اعداد شبه تصادفی اعداد تصادفی در استاندارد C C اعداد تصادفی از 0 تا 100

دنباله ای از اعداد متشکل از عناصر عملاً مستقل داریم که از توزیع معینی تبعیت می کنند. به عنوان یک قاعده، توزیع یکنواخت.

شما می توانید اعداد تصادفی را در اکسل به روش ها و روش های مختلف تولید کنید. بیایید فقط بهترین آنها را در نظر بگیریم.

تابع اعداد تصادفی در اکسل

  1. تابع RAND یک عدد واقعی تصادفی و یکنواخت را برمی گرداند. کمتر از 1، بزرگتر یا مساوی 0 خواهد بود.
  2. تابع RANDBETWEEN یک عدد صحیح تصادفی برمی گرداند.

بیایید با مثال به کاربرد آنها نگاه کنیم.

نمونه برداری از اعداد تصادفی با استفاده از RAND

این تابع به آرگومان نیاز ندارد (RAND()).

به عنوان مثال، برای ایجاد یک عدد واقعی تصادفی در محدوده 1 تا 5، از فرمول زیر استفاده کنید: =RAND()*(5-1)+1.

عدد تصادفی برگشتی به طور یکنواخت در بازه زمانی توزیع می شود.

هر بار که کاربرگ محاسبه می شود یا مقدار هر سلول در کاربرگ تغییر می کند، یک عدد تصادفی جدید برگردانده می شود. اگر می خواهید جمعیت تولید شده را ذخیره کنید، می توانید فرمول را با مقدار آن جایگزین کنید.

  1. روی سلول با یک عدد تصادفی کلیک کنید.
  2. در نوار فرمول، فرمول را انتخاب کنید.
  3. F9 را فشار دهید. و وارد کنید.

بیایید یکنواختی توزیع اعداد تصادفی از اولین نمونه را با استفاده از یک هیستوگرام توزیع بررسی کنیم.


محدوده مقادیر عمودی فرکانس است. افقی - "جیب".



تابع RANDBETWEEN

نحو تابع RANDBETWEEN (کران پایین، کران بالا) است. آرگومان اول باید کمتر از آرگومان دوم باشد. در غیر این صورت تابع یک خطا ایجاد می کند. مرزها اعداد صحیح فرض می شوند. فرمول قسمت کسری را کنار می گذارد.

مثال استفاده از تابع:

اعداد تصادفی با دقت 0.1 و 0.01:

چگونه یک مولد اعداد تصادفی در اکسل بسازیم

بیایید یک مولد اعداد تصادفی بسازیم که مقداری را از محدوده خاصی تولید می کند. ما از فرمولی مانند: =INDEX(A1:A10,INTEGER(RAND()*10)+1 استفاده می کنیم.

بیایید یک مولد اعداد تصادفی در محدوده 0 تا 100 در مراحل 10 بسازیم.

شما باید 2 مورد تصادفی را از لیست مقادیر متن انتخاب کنید. با استفاده از تابع RAND، مقادیر متن را در محدوده A1:A7 با اعداد تصادفی مقایسه می کنیم.

بیایید از تابع INDEX برای انتخاب دو مقدار متن تصادفی از لیست اصلی استفاده کنیم.

برای انتخاب یک مقدار تصادفی از لیست، از فرمول زیر استفاده کنید: =INDEX(A1:A7,RANDBETWEEN(1,COUNT(A1:A7))).

توزیع نرمال مولد اعداد تصادفی

توابع RAND و RANDBETWEEN اعداد تصادفی را با توزیع یکنواخت تولید می کنند. هر مقدار با احتمال یکسان می تواند در حد پایینی محدوده درخواستی و در حد بالایی قرار گیرد. این منجر به اسپرد عظیمی از ارزش هدف می شود.

توزیع نرمال به این معنی است که اکثر اعداد تولید شده نزدیک به عدد هدف هستند. بیایید فرمول RANDBETWEEN را تنظیم کنیم و یک آرایه داده با توزیع نرمال ایجاد کنیم.

هزینه محصول X 100 روبل است. کل دسته تولید شده از توزیع نرمال پیروی می کند. یک متغیر تصادفی نیز از توزیع احتمال نرمال پیروی می کند.

در چنین شرایطی، مقدار متوسط ​​محدوده 100 روبل است. بیایید یک آرایه ایجاد کنیم و یک نمودار با توزیع نرمال با انحراف استاندارد 1.5 روبل بسازیم.

ما از تابع: =NORMINV(RAND();100;1.5) استفاده می کنیم.

اکسل محاسبه کرد که چه مقادیری در محدوده احتمال قرار دارند. از آنجایی که احتمال تولید یک محصول با هزینه 100 روبل حداکثر است، فرمول مقادیر نزدیک به 100 را بیشتر از سایرین نشان می دهد.

بیایید به رسم نمودار برویم. ابتدا باید یک جدول با دسته ها ایجاد کنید. برای انجام این کار، آرایه را به دوره های زیر تقسیم می کنیم:

بر اساس داده های به دست آمده، می توانیم نموداری با توزیع نرمال ایجاد کنیم. محور ارزش تعداد متغیرها در بازه است، محور دسته دوره ها است.

ترجمه مقاله اعداد تصادفی توسط Jon Skeete که در محافل باریک شناخته شده است. من در این مقاله متوقف شدم زیرا در یک زمان من خودم با مشکل توضیح داده شده در آن مواجه شدم.

مرور موضوعات توسط .خالصو سی شارپدر وب‌سایت StackOverflow، می‌توانید سؤالات بی‌شماری با ذکر کلمه «تصادفی» مشاهده کنید، که در واقع همان سؤال ابدی و «نابودی» را مطرح می‌کند: چرا مولد اعداد تصادفی System.Random «کار نمی‌کند» و چگونه « درستش کن"" این مقاله به بررسی این مشکل و راه های حل آن اختصاص دارد.

فرمول بندی مسئله

در StackOverflow، در گروه‌های خبری و لیست‌های پستی، همه سؤالات در مورد موضوع "تصادفی" چیزی شبیه به این هستند:
من از Random.Next برای تولید اعداد تصادفی متعدد استفاده می‌کنم، اما این روش زمانی که چندین بار فراخوانی می‌شود همان عدد را برمی‌گرداند. هر بار که برنامه راه اندازی می شود، عدد تغییر می کند، اما در یک اجرای برنامه ثابت است.

یک کد مثال چیزی شبیه به این است:
// کد بد! استفاده نکن! برای (int i = 0; i< 100; i++) { Console.WriteLine(GenerateDigit()); } ... static int GenerateDigit() { Random rng = new Random(); // Предположим, что здесь много логики return rng.Next(10); }
پس اینجا چه اشکالی دارد؟

توضیح

کلاس Random یک مولد اعداد تصادفی واقعی نیست، بلکه شامل یک مولد است شبهاعداد تصادفی هر نمونه از کلاس Random شامل یک حالت داخلی است و زمانی که متد Next (یا NextDouble یا NextBytes) فراخوانی می شود، متد از آن حالت برای برگرداندن عددی استفاده می کند که تصادفی ظاهر می شود. سپس حالت داخلی تغییر می‌کند تا دفعه بعد که Next فراخوانی می‌شود، یک عدد ظاهراً تصادفی متفاوت از آنچه قبلاً برگردانده شده است، برمی‌گرداند.

همه «داخلی‌های» کلاس Random کاملا قطعی. این بدان معناست که اگر چندین نمونه از کلاس Random را با همان حالت اولیه بگیرید که از طریق پارامتر سازنده مشخص شده است. دانهو برای هر نمونه متدهای خاصی را به همان ترتیب و با پارامترهای یکسان فراخوانی کنید، سپس در پایان نتایج یکسانی خواهید داشت.

خب کد بالا چه اشکالی دارد؟ نکته بد این است که ما از یک نمونه جدید از کلاس Random در داخل هر تکرار حلقه استفاده می کنیم. سازنده تصادفی که هیچ پارامتری نمی گیرد، مقدار را می گیرد تاریخ فعلیو زمان به عنوان دانه (حالت اولیه). تکرارها در حلقه آنقدر سریع "پیمایش" خواهند کرد که زمان سیستمپس از تکمیل آنها "زمانی برای تغییر نخواهند داشت". بنابراین، همه نمونه‌های Random همان مقدار حالت اولیه خود را دریافت می‌کنند و بنابراین همان عدد شبه تصادفی را برمی‌گردانند.

چطوری میشه اینو تعمیر کرد؟

راه حل های زیادی برای حل این مشکل وجود دارد که هر کدام مزایا و معایب خاص خود را دارند. ما به چند مورد از آنها نگاه خواهیم کرد.
استفاده از مولد اعداد تصادفی رمزنگاری
دات نت حاوی یک کلاس انتزاعی RandomNumberGenerator است که تمامی پیاده سازی های مولد اعداد تصادفی رمزنگاری (که از این پس به عنوان cryptoRNG نامیده می شود) باید از آن به ارث برسند. .NET همچنین شامل یکی از این پیاده سازی ها است - کلاس RNGCryptoServiceProvider را ملاقات کنید. ایده رمزارز RNG این است که حتی اگر هنوز یک تولید کننده اعداد شبه تصادفی باشد، نتایج غیرقابل پیش بینی نسبتاً قوی را ارائه می دهد. RNGCryptoServiceProvider از چندین منبع آنتروپی استفاده می کند که اساساً "نویز" در رایانه شما هستند و پیش بینی توالی اعدادی که تولید می کند بسیار دشوار است. علاوه بر این، نویز "در کامپیوتر" نه تنها به عنوان حالت اولیه، بلکه بین تماس‌های شماره‌های تصادفی بعدی نیز قابل استفاده است. بنابراین، حتی با دانستن وضعیت فعلی کلاس، محاسبه اعداد بعدی که در آینده تولید خواهند شد و اعدادی که قبلا تولید شده اند کافی نخواهد بود. در واقع، رفتار دقیق به اجرا وابسته است. علاوه بر این، ویندوز می تواند از تخصصی استفاده کند سخت افزار، که منبعی از "تصادفی واقعی" است (به عنوان مثال، می تواند یک حسگر واپاشی ایزوتوپ رادیواکتیو باشد) تا اعداد تصادفی ایمن و قابل اعتمادتری تولید کند.

بیایید این را با کلاس Random که قبلا بحث شد مقایسه کنیم. فرض کنید ده بار با Random.Next(100) تماس گرفتید و نتایج را ذخیره کردید. اگر قدرت محاسباتی کافی دارید، می توانید تنها بر اساس این نتایج، حالت اولیه (seed) را که نمونه تصادفی با آن ایجاد شده است، محاسبه کنید، نتایج بعدی فراخوانی Random.Next(100) را پیش بینی کنید، و حتی نتایج را محاسبه کنید. فراخوانی های روش قبلی اگر از اعداد تصادفی برای اهداف امنیتی، مالی و غیره استفاده می کنید، این رفتار به شدت غیرقابل قبول است. Crypto RNGها به طور قابل توجهی کندتر از کلاس Random کار می کنند، اما دنباله ای از اعداد را تولید می کنند که هر کدام از مقادیر دیگر مستقل تر و غیرقابل پیش بینی تر هستند.

در بیشتر موارد، عملکرد ضعیف مانع از معامله نیست - یک API بد است. RandomNumberGenerator برای تولید توالی بایت طراحی شده است - این همه است. این را با متدهای کلاس Random مقایسه کنید، جایی که می توان یک عدد صحیح تصادفی، عدد کسری و همچنین مجموعه ای از بایت ها را بدست آورد. یکی دیگر دارایی مفید- امکان به دست آوردن یک عدد تصادفی در یک محدوده مشخص. این احتمالات را با آرایه بایت های تصادفی که RandomNumberGenerator تولید می کند مقایسه کنید. می‌توانید با ایجاد wrapper (wrapper) خود در اطراف RandomNumberGenerator وضعیت را اصلاح کنید، که بایت‌های تصادفی را به یک نتیجه «مناسب» تبدیل می‌کند، اما این راه‌حل بی‌اهمیت است.

با این حال، در بیشتر موارد، اگر بتوانید مشکلی را که در ابتدای مقاله توضیح داده شد، حل کنید، "ضعف" کلاس Random خوب است. بیایید ببینیم در اینجا چه کاری می توانیم انجام دهیم.

از یک نمونه از کلاس Random برای چندین تماس استفاده کنید
در اینجا، ریشه راه حل مشکل استفاده از تنها یک نمونه از Random هنگام ایجاد تعداد زیادی اعداد تصادفی با استفاده از Random.Next است. و این بسیار ساده است - ببینید چگونه می توانید کد بالا را تغییر دهید:
// این کد بهتر خواهد بود Random rng = new Random(); برای (int i = 0; i< 100; i++) { Console.WriteLine(GenerateDigit(rng)); } ... static int GenerateDigit(Random rng) { // Предположим, что здесь много логики return rng.Next(10); }
حالا هر تکرار اعداد متفاوتی خواهد داشت... اما این همه چیز نیست. اگر این بلوک کد را دو بار پشت سر هم فراخوانی کنیم چه اتفاقی می افتد؟ درست است، ما دو نمونه تصادفی با همان دانه ایجاد می کنیم و دو مجموعه یکسان از اعداد تصادفی به دست می آوریم. اعداد در هر مجموعه متفاوت خواهد بود، اما این مجموعه ها در بین خود برابر خواهند بود.

دو راه برای حل مشکل وجود دارد. ابتدا می‌توانیم از یک نمونه استفاده نکنیم، بلکه از یک فیلد ثابت حاوی نمونه‌ای از Random استفاده کنیم و سپس کد بالا تنها یک نمونه ایجاد می‌کند و از آن استفاده می‌کند و هر چند بار که لازم است آن را فراخوانی می‌کند. ثانیاً، می‌توانیم ایجاد یک نمونه تصادفی را از آنجا به طور کامل حذف کنیم، و آن را "بالاتر" منتقل کنیم، در حالت ایده‌آل به همان "بالای" برنامه، جایی که یک نمونه تصادفی واحد ایجاد می‌شود، پس از آن به همه مکان‌ها منتقل می‌شود. جایی که به اعداد تصادفی نیاز است. این یک ایده عالی است که به خوبی توسط وابستگی ها بیان می شود، اما تا زمانی که فقط از یک رشته استفاده کنیم، کار خواهد کرد.

ایمنی نخ

کلاس Random ایمن نیست. با در نظر گرفتن اینکه چقدر دوست داریم یک نمونه واحد بسازیم و از آن در طول یک برنامه برای کل مدت اجرای آن استفاده کنیم (تک تک، سلام!)، عدم ایمنی نخ تبدیل به دردسر واقعی می شود. به هر حال، اگر از یک نمونه به طور همزمان در چندین رشته استفاده کنیم، احتمال بازنشانی حالت داخلی آن وجود دارد و اگر این اتفاق بیفتد، از آن لحظه به بعد نمونه بی‌فایده می‌شود.

باز هم دو راه برای حل مشکل وجود دارد. مسیر اول همچنان شامل استفاده از یک نمونه واحد است، اما این بار با استفاده از قفل کردن منابع از طریق مانیتور. برای انجام این کار، باید یک پوشش در اطراف Random ایجاد کنید که تماس‌های متدهای خود را در یک عبارت قفل قرار دهد و دسترسی انحصاری تماس‌گیرنده به نمونه را تضمین کند. این مسیر بد است زیرا عملکرد را در سناریوهای فشرده با نخ کاهش می دهد.

راه دیگری که در زیر توضیح خواهم داد، استفاده از یک نمونه در هر رشته است. تنها چیزی که باید مطمئن شویم این است که هنگام ایجاد نمونه از seed های مختلف استفاده می کنیم، بنابراین نمی توانیم از سازنده های پیش فرض استفاده کنیم. در غیر این صورت، این مسیر نسبتاً ساده است.

ارائه دهنده ایمن

خوشبختانه کلاس عمومی جدید ThreadLocal ، که در .NET 4 معرفی شده است، نوشتن ارائه دهندگانی را که یک نمونه در هر رشته ارائه می کنند بسیار آسان می کند. شما فقط باید یک نماینده به سازنده ThreadLocal ارسال کنید، که به دریافت مقدار خود نمونه ما اشاره دارد. در این مورد، من تصمیم گرفتم از یک مقدار seed استفاده کنم و آن را با استفاده از Environment.TickCount (که دقیقاً چگونه سازنده تصادفی بدون پارامتر کار می کند) مقداردهی اولیه کنم. در مرحله بعد، هر بار که نیاز داریم یک نمونه تصادفی جدید برای یک رشته جداگانه بدست آوریم، تعداد تیک های حاصل افزایش می یابد.

کلاس زیر کاملا ثابت است و فقط شامل یک متد عمومی (باز) GetThreadRandom است. این متد به‌جای یک ویژگی، به‌عنوان یک متد ساخته می‌شود، عمدتاً برای راحتی: این تضمین می‌کند که همه کلاس‌هایی که به نمونه‌ای از Random نیاز دارند، به Func بستگی دارند. (نماینده ای که به متدی اشاره می کند که هیچ پارامتری نمی گیرد و مقداری از نوع Random برمی گرداند)، و نه از خود کلاس Random. اگر یک نوع در نظر گرفته شده است که روی یک رشته واحد اجرا شود، می‌تواند یک نماینده را برای بدست آوردن یک نمونه تصادفی فراخوانی کند و سپس از آن در سراسر استفاده کند. اگر نوع نیاز به کار در سناریوهای چند رشته ای داشته باشد، می تواند هر بار که به یک مولد اعداد تصادفی نیاز دارد، نماینده را صدا کند. کلاس زیر به تعداد thread ها نمونه های کلاس Random ایجاد می کند و هر نمونه از مقدار اولیه متفاوتی شروع می شود. اگر نیاز به استفاده از ارائه‌دهنده اعداد تصادفی به عنوان یک وابستگی در انواع دیگر داشته باشیم، می‌توانیم این کار را انجام دهیم: TypeThatNeedsRandom(RandomProvider.GetThreadRandom) جدید. خب، این خود کد است:
با استفاده از سیستم؛ با استفاده از System.Threading. کلاس استاتیک عمومی RandomProvider ( int static private seed = Environment.TickCount؛ ThreadLocal استاتیک خصوصی randomWrapper = ThreadLocal جدید (() => new Random(Interlocked.Increment(ref seed))); عمومی استاتیک تصادفی GetThreadRandom() ( randomWrapper.Value; ) )
به اندازه کافی ساده، درست است؟ این به این دلیل است که تمام کدها با هدف تولید نمونه صحیح Random هستند. هنگامی که یک نمونه ایجاد و برگردانده شد، مهم نیست که بعداً با آن چه کاری انجام می دهید: تمام موارد صدور نمونه های بعدی کاملاً مستقل از نمونه فعلی هستند. البته، کد کلاینت یک حفره برای سوء استفاده مخرب دارد: می‌تواند یک نمونه از Random را بگیرد و به جای فراخوانی RandomProvider ما در آن رشته‌های دیگر، آن را به رشته‌های دیگر ارسال کند.

مشکلات طراحی رابط

یک مشکل هنوز باقی است: ما از یک مولد اعداد تصادفی محافظت شده ضعیف استفاده می کنیم. همانطور که قبلا ذکر شد، نسخه بسیار امن تری از RNG در RandomNumberGenerator وجود دارد که پیاده سازی آن در کلاس RNGCryptoServiceProvider است. با این حال، API آن برای استفاده در سناریوهای استاندارد بسیار دشوار است.

بسیار خوب است اگر ارائه دهندگان RNG در چارچوب «منابع تصادفی» جداگانه داشته باشند. در این مورد، ما می‌توانیم یک API واحد، ساده و راحت داشته باشیم که هم با اجرای ناامن اما سریع و هم با اجرای امن اما آهسته پشتیبانی می‌شود. خوب، خواب دیدن ضرری ندارد. شاید عملکرد مشابهی در نسخه های بعدی .NET Framework ظاهر شود. شاید کسی که از مایکروسافت نیست، اجرای خود را از آداپتور ارائه دهد. (متاسفانه، من آن شخص نخواهم بود... پیاده سازی درست چنین چیزی به طرز شگفت انگیزی پیچیده است.) همچنین می توانید کلاس خود را با استخراج از Random و نادیده گرفتن متدهای Sample و NextBytes ایجاد کنید، اما دقیقاً مشخص نیست که چگونه باید آنها را انجام دهند. کار، یا حتی نمونه پیاده سازی خودتان می تواند بسیار پیچیده تر از آن چیزی باشد که به نظر می رسد. شاید دفعه بعد…

لطفا مکث کنید AdBlock کار می کنددر آن وب سایت

گاهی ممکن است نیاز به تولید اعداد تصادفی باشد. یک مثال ساده

مثال: تعیین برنده در مسابقه ارسال مجدد.

یک لیست 53 نفره وجود دارد. انتخاب برنده از بین آنها ضروری است. اگر خودتان آن را انتخاب کنید، ممکن است به تعصب متهم شوید. بنابراین تصمیم می گیرید یک برنامه بنویسید. به صورت زیر عمل خواهد کرد. شما تعداد شرکت کنندگان N را وارد می کنید، پس از آن برنامه یک عدد را نمایش می دهد - شماره برنده.

شما قبلاً می دانید چگونه از یک بازیکن شماره بگیرید. اما چگونه می توان کامپیوتر را مجبور کرد که به یک عدد تصادفی فکر کند؟ در این درس یاد خواهید گرفت که چگونه این کار را انجام دهید.

تابع rand().

این تابع یک عدد صحیح تصادفی را در محدوده صفر تا RAND_MAX برمی گرداند. RAND_MAX یک ثابت خاص C است که حداکثر مقدار صحیحی را که می توان توسط تابع rand() برگرداند را در خود نگه می دارد.

تابع rand() در فایل هدر stdlib.h تعریف شده است. بنابراین، اگر می خواهید از رند در برنامه خود استفاده کنید، فراموش نکنید که این فایل هدر را قرار دهید. ثابت RAND_MAX نیز در این فایل تعریف شده است. شما می توانید این فایل را در کامپیوتر خود پیدا کنید و معنی آن را ببینید.

بیایید این ویژگی را در عمل ببینیم. بیایید کد زیر را اجرا کنیم:

فهرست 1.

#عبارتند از // برای استفاده از تابع printf #include // برای استفاده از تابع rand int main(void) ( /* تولید پنج عدد صحیح تصادفی */ printf("%d\n", rand()); printf("%d\n", rand()); printf ("%d\n"، rand()); printf("%d\n"، rand())؛ printf("%d\n"، rand()); )

باید چیزی شبیه این به نظر برسد.

شکل 1 پنج عدد تصادفی تولید شده توسط تابع رند

اما ما دوست داریم اعداد از 1 تا 53 را بدست آوریم و نه همه چیز را پشت سر هم. در اینجا چند ترفند وجود دارد که به شما کمک می کند تا تابع rand() را محدود کنید.

اعداد تصادفی را از بالا محدود کنید.

هرکسی که در مدرسه منتظر لحظه ای بود که ریاضیات به کارتان بیاید، آماده شود. لحظه فرا رسیده است. برای محدود کردن اعداد تصادفی از بالا می توانید از عملیات بدست آوردن باقیمانده تقسیم که در درس گذشته یاد گرفتید استفاده کنید. احتمالاً می دانید که باقیمانده تقسیم بر اعداد K همیشه کمتر از عدد K است. به عنوان مثال، تقسیم بر 4 می تواند باقیمانده های 0، 1، 2 و 3 باشد. بنابراین، اگر می خواهید اعداد تصادفی را از بالا به عدد K محدود کنید، به سادگی باقیمانده تقسیم بر K را بگیرید. مثل این:

لیست 2.

#عبارتند از #عبارتند از int main(void) ( /* تولید پنج عدد صحیح تصادفی کمتر از 100 */ printf("%d\n", rand()%100); printf("%d\n", rand()%100); printf ("%d\n"، rand()%100); printf("%d\n"، rand()%100); printf("%d\n"، rand()%100); )


شکل 2 پنج عدد تصادفی کمتر از 100

اعداد زیر را محدود کنید

تابع rand اعداد تصادفی را از بازه باز می گرداند. اگر فقط به اعداد بزرگتر از M (مثلاً 1000) نیاز داشته باشیم، چه؟ باید چکار کنم؟ ساده است. بیایید فقط مقدار M را به چیزی که تابع rand برمی گرداند اضافه کنیم. سپس اگر تابع 0 را برگرداند، پاسخ نهایی M و اگر 2394 باشد، پاسخ نهایی M + 2394 خواهد بود. با این عمل به نظر می رسد همه اعداد را با M واحد به جلو منتقل می کنیم.

مرزهای بالا و پایین تابع رند را تنظیم کنید.

به عنوان مثال اعداد 80 تا 100 را بدست آورید. به نظر می رسد فقط باید دو روش بالا را با هم ترکیب کنید. ما چیزی شبیه به این دریافت خواهیم کرد:

لیست 3.

#عبارتند از #عبارتند از int main(void) ( /* تولید پنج عدد صحیح تصادفی بزرگتر از 80 و کمتر از 100 */printf("%d\n", 80 + rand()%100); printf("%d\n"، 80 + rand ()%100); printf("%d\n"، 80 + rand()%100); printf("%d\n"، 80 + rand()%100); printf("%d\n " , 80 + rand()%100);)

سعی کنید این برنامه را اجرا کنید. غافلگیر شدن؟

بله، این روش کار نخواهد کرد. بیایید این برنامه را با دست اجرا کنیم تا ببینیم اشتباه کرده ایم. فرض کنید rand() عدد 143 را برگرداند. باقیمانده وقتی بر 100 تقسیم شود 43 می شود. سپس 80 + 43 = 123. بنابراین این روش کار نمی کند. یک طرح مشابه اعداد از 80 تا 179 را تولید می کند.

بیایید گام به گام بیان خود را بشکنیم. rand()%100 می تواند اعداد از 0 تا 99 را شامل شود. آن ها از بخش
Operation + 80 بخش ما را 80 واحد به سمت راست تغییر می دهد. ما گرفتیم.
همانطور که می بینید، مشکل ما در مرز سمت راست سگمنت است؛ 79 واحد به سمت راست منتقل شده است. این عدد اصلی ما 80 منهای 1 است. بیایید همه چیز را تمیز کنیم و حاشیه سمت راست را به عقب برگردانیم: 80 + rand()% (100 - 80 + 1) . سپس همه چیز باید همانطور که باید کار کند.

که در مورد کلیاگر نیاز به گرفتن اعداد از بخش داریم، باید از ساختار زیر استفاده کنیم:
A + rand()% (B-A+1).

با توجه به این فرمول، ما را بازنویسی می کنیم آخرین برنامه:

لیست 4.

#عبارتند از #عبارتند از int main(void) ( /* تولید پنج عدد صحیح تصادفی از بخش */ printf("%d\n", 80 + rand()%(100 - 80 + 1)); printf("%d\n", 80 + rand()%(100 - 79)); printf("%d\n"، 80 + rand()%21); printf("%d\n"، 80 + rand()%21); printf ("%d\n"، 80 + rand()%21)؛ )

نتیجه:


شکل 3 اعداد تصادفی از یک محدوده

خب حالا می توانید مشکل اصلی درس را حل کنید. یک عدد از یک بخش تولید کنید. یا نمی توانید؟

اما ابتدا کمی بیشتر اطلاعات مفید. آخرین برنامه را سه بار پشت سر هم اجرا کنید و اعداد تصادفی تولید شده را یادداشت کنید. متوجه شدید؟

تابع srand().

بله، هر بار همان اعداد یکسان ظاهر می شوند. "چنین ژنراتور!" - تو بگو. و کاملا درست نخواهید بود. در واقع، اعداد یکسان همیشه تولید می شوند. اما ما می‌توانیم با استفاده از تابع srand () که در فایل هدر stdlib.h نیز تعریف شده است، روی آن تأثیر بگذاریم. مولد اعداد تصادفی را با یک عدد seed مقداردهی اولیه می کند.

این برنامه را چندین بار کامپایل و اجرا کنید:

لیست 5.

#عبارتند از #عبارتند از int main(void) (srand(2); /* تولید پنج عدد صحیح تصادفی از بخش */ printf("%d\n", 80 + rand()%(100 - 80 + 1)); printf("% d\n"، 80 + rand()%(100 - 79)); printf("%d\n"، 80 + rand()%21); printf("%d\n"، 80 + rand() %21); printf("%d\n"، 80 + rand()%21); )

حالا آرگومان تابع srand() را به عدد دیگری تغییر دهید (امیدوارم فراموش نکرده باشید که آرگومان تابع چیست؟) و دوباره برنامه را کامپایل و اجرا کنید. دنباله اعداد باید تغییر کند. به محض تغییر آرگومان در تابع srand، دنباله نیز تغییر می کند. خیلی کاربردی نیست، اینطور است؟ برای تغییر دنباله، باید برنامه را دوباره کامپایل کنید. اگر فقط این شماره به طور خودکار در آنجا درج شود.

و قابل انجام است. مثلاً استفاده کنیم تابع زمان() که در فایل هدر time.h تعریف شده است. این تابع، اگر NULL به عنوان آرگومان ارسال شود، تعداد ثانیه هایی که از اول ژانویه 1970 گذشته است را برمی گرداند. در اینجا نگاهی به نحوه انجام آن داریم.

لیست 6.

#عبارتند از #عبارتند از #عبارتند از // برای استفاده از تابع time() int main(void) ( srand(time(NULL)); /* تولید پنج عدد صحیح تصادفی از بخش */ printf("%d\n", 80 + rand()%( 100 - 80 + 1)); printf("%d\n", 80 + rand()%(100 - 79)); printf("%d\n"، 80 + rand()%21); printf( "%d\n"، 80 + rand()%21); printf("%d\n"، 80 + rand()%21); )

ممکن است بپرسید NULL چیست؟ یک سوال منطقی در ضمن، من به شما خواهم گفت که این کلمه اختصاصی خاص چیست. همچنین می توانم بگویم اشاره گر تهی به چه معناست، اما ... پس این هیچ اطلاعاتی در اختیار شما قرار نمی دهد این لحظهتوصیه می کنم به آن فکر نکنید. و فقط آن را به عنوان نوعی ترفند هوشمندانه به خاطر بسپارید. در درس های آینده با جزئیات بیشتری به این موضوع خواهیم پرداخت.

اغلب در برنامه ها نیاز به استفاده از اعداد تصادفی وجود دارد - از پر کردن یک آرایه تا رمزنگاری. برای بدست آوردن دنباله ای از اعداد تصادفی، زبان سی شارپ دارای کلاس Random است. این کلاس دو سازنده ارائه می دهد:

  • تصادفی()- نمونه ای از کلاس Random را با مقدار اولیه که به زمان فعلی بستگی دارد مقداردهی اولیه می کند. همانطور که مشخص است، زمان را می توان در نمایش داد کنه ها- 100 پالس نانوثانیه از 1 ژانویه 0001 شروع می شود. و مقدار زمان در تیک ها یک عدد صحیح 64 بیتی است که برای مقداردهی اولیه نمونه مولد اعداد تصادفی استفاده می شود.
  • تصادفی (Int32)- یک نمونه از کلاس Random را با استفاده از مقدار اولیه مشخص شده مقداردهی اولیه می کند. راه‌اندازی یک مولد اعداد تصادفی به این روش می‌تواند هنگام اشکال‌زدایی یک برنامه راحت باشد، زیرا در این مورد هر بار که برنامه راه‌اندازی می‌شود همان اعداد "تصادفی" تولید می‌شوند.
متد اصلی این کلاس متد Next() است که به شما امکان می دهد یک عدد تصادفی بدست آورید و تعدادی اضافه بار دارد:
  • Next() - یک عدد صحیح غیر منفی تصادفی در قالب Int32 برمی گرداند.
  • بعد( Int32)- یک عدد صحیح غیر منفی تصادفی که کمتر از مقدار مشخص شده است را برمی گرداند.
  • بعد( Int32 دقیقه، Int32 حداکثر)- یک عدد صحیح تصادفی در محدوده مشخص شده برمی گرداند. در این صورت شرط min باید رعایت شود
و همچنین روش ها
  • NextBytes( بایت)- عناصر آرایه بایت مشخص شده را با اعداد تصادفی پر می کند.
  • NextDouble() - یک عدد ممیز شناور تصادفی را در محدوده برمی گرداند.
    زنگ تفريح ؛ // مطابقت پیدا شد، عنصر مطابقت ندارد
    }
    اگر (j == i)
    { // مطابقت پیدا نشد
    a[i] = تعداد; // عنصر را ذخیره کنید
    i++; // به عنصر بعدی بروید
    }
    }
    برای (int i = 0; i< 100; i++)
    {

    اگر (i % 10 == 9)
    Console.WriteLine();
    }
    کنسول .ReadKey();
    }
    }
    }

    با این حال، هر چه به انتهای آرایه نزدیک‌تر باشد، باید نسل‌های بیشتری برای به دست آوردن یک مقدار غیر تکراری انجام شود.
    مثال زیر تعداد فراخوانی‌های متد Next() برای به دست آوردن هر عنصر و همچنین تعداد کل اعداد تصادفی تولید شده برای پر کردن یک آرایه 100 عنصری با مقادیر غیر تکراری را نشان می‌دهد.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53

    با استفاده از سیستم؛
    فضای نام MyProgram
    {
    برنامه کلاس
    {
    فضای خالی استاتیک اصلی (آرگس های رشته ای)
    {
    تصادفی rnd = new Random();
    int a = int جدید ; // آرایه ای از عناصر
    int count = int جدید ; // آرایه تعداد نسل
    a = rnd.Next(0, 101);
    int c = 0; // شمارنده تعداد نسل ها
    تعداد = 1; // a فقط یک بار تولید می شود
    برای (int i = 1; i< 100;)
    {
    int num = rnd.Next(0, 101);
    c++; // یک بار دیگر عنصر را تولید کرد
    int j;
    برای (j = 0؛ j< i; j++)
    {
    اگر (تعداد == a[j])
    زنگ تفريح ؛
    }
    اگر (j == i)
    {
    a[i] = تعداد; i++;
    count[i] = c; c = 0; // تعداد نسل ها را ذخیره کنید
    }
    }
    // چاپ مقادیر عناصر
    کنسول .WriteLine( "ارزش عناصر");
    برای (int i = 0; i< 100; i++)
    {
    Console .Write("(0,4) " , a[i]);
    اگر (i % 10 == 9)
    Console.WriteLine();
    }
    Console.WriteLine();
    // نمایش تعداد نسل ها
    کنسول .WriteLine( "تعداد تولید عنصر");
    int sum = 0;
    برای (int i = 0; i< 100; i++)
    {
    sum += count[i];
    Console .Write("(0,4) " , count[i]);
    اگر (i % 10 == 9)
    Console.WriteLine();
    }
    کنسول .WriteLine( "تعداد کل نسل - (0)"، مجموع)؛
    کنسول .ReadKey();
    }
    }
    }

    void Main (ارگ های رشته ای)
    {
    تصادفی rnd = new Random();
    int a = جدید int ;
    برای (int i = 0; i< 100; i++)
    a[i] = i;
    برای (int i = 0; i< 50; i++)
    {
    int i1 = rnd.Next(0, 100); // اولین شاخص
    int i2 = rnd.Next(0, 100); // شاخص دوم
    // تبادل مقادیر عناصر با شاخص های i1 و i2
    int temp = a;
    a = a;
    a = دما
    }
    کنسول .WriteLine( "ارزش عناصر");
    برای (int i = 0; i< 100; i++)
    {
    Console .Write("(0,4) " , a[i]);
    اگر (i % 10 == 9)
    Console.WriteLine();
    }
    کنسول .ReadKey();
    }
    }
    }

    اگر دامنه مقادیر با تعداد مقادیر مطابقت داشته باشد (یا نزدیک به) باشد، مخلوط کردن مقادیر مؤثرتر است، زیرا در این حالت تعداد تولید عناصر تصادفی به میزان قابل توجهی کاهش می یابد.

    ترجمه مقاله اعداد تصادفی توسط Jon Skeete که در محافل باریک شناخته شده است. من در این مقاله متوقف شدم زیرا در یک زمان من خودم با مشکل توضیح داده شده در آن مواجه شدم.

    مرور موضوعات توسط .خالصو سی شارپدر وب‌سایت StackOverflow، می‌توانید سؤالات بی‌شماری با ذکر کلمه «تصادفی» مشاهده کنید، که در واقع همان سؤال ابدی و «نابودی» را مطرح می‌کند: چرا مولد اعداد تصادفی System.Random «کار نمی‌کند» و چگونه « درستش کن"" این مقاله به بررسی این مشکل و راه های حل آن اختصاص دارد.

    فرمول بندی مسئله

    در StackOverflow، در گروه‌های خبری و لیست‌های پستی، همه سؤالات در مورد موضوع "تصادفی" چیزی شبیه به این هستند:
    من از Random.Next برای تولید اعداد تصادفی متعدد استفاده می‌کنم، اما این روش زمانی که چندین بار فراخوانی می‌شود همان عدد را برمی‌گرداند. هر بار که برنامه راه اندازی می شود، عدد تغییر می کند، اما در یک اجرای برنامه ثابت است.

    یک کد مثال چیزی شبیه به این است:
    // کد بد! استفاده نکن! برای (int i = 0; i< 100; i++) { Console.WriteLine(GenerateDigit()); } ... static int GenerateDigit() { Random rng = new Random(); // Предположим, что здесь много логики return rng.Next(10); }
    پس اینجا چه اشکالی دارد؟

    توضیح

    کلاس Random یک مولد اعداد تصادفی واقعی نیست، بلکه شامل یک مولد است شبهاعداد تصادفی هر نمونه از کلاس Random شامل یک حالت داخلی است و زمانی که متد Next (یا NextDouble یا NextBytes) فراخوانی می شود، متد از آن حالت برای برگرداندن عددی استفاده می کند که تصادفی ظاهر می شود. سپس حالت داخلی تغییر می‌کند تا دفعه بعد که Next فراخوانی می‌شود، یک عدد ظاهراً تصادفی متفاوت از آنچه قبلاً برگردانده شده است، برمی‌گرداند.

    همه «داخلی‌های» کلاس Random کاملا قطعی. این بدان معناست که اگر چندین نمونه از کلاس Random را با همان حالت اولیه بگیرید که از طریق پارامتر سازنده مشخص شده است. دانهو برای هر نمونه متدهای خاصی را به همان ترتیب و با پارامترهای یکسان فراخوانی کنید، سپس در پایان نتایج یکسانی خواهید داشت.

    خب کد بالا چه اشکالی دارد؟ نکته بد این است که ما از یک نمونه جدید از کلاس Random در داخل هر تکرار حلقه استفاده می کنیم. سازنده تصادفی، که هیچ پارامتری را نمی گیرد، تاریخ و زمان فعلی را به عنوان بذر خود می گیرد. تکرارها در حلقه به سرعت "پیمایش" می شوند که زمان سیستم "زمانی برای تغییر نخواهد داشت" پس از تکمیل آنها. بنابراین، همه نمونه‌های Random همان مقدار حالت اولیه خود را دریافت می‌کنند و بنابراین همان عدد شبه تصادفی را برمی‌گردانند.

    چطوری میشه اینو تعمیر کرد؟

    راه حل های زیادی برای حل این مشکل وجود دارد که هر کدام مزایا و معایب خاص خود را دارند. ما به چند مورد از آنها نگاه خواهیم کرد.
    استفاده از مولد اعداد تصادفی رمزنگاری
    دات نت حاوی یک کلاس انتزاعی RandomNumberGenerator است که تمامی پیاده سازی های مولد اعداد تصادفی رمزنگاری (که از این پس به عنوان cryptoRNG نامیده می شود) باید از آن به ارث برسند. .NET همچنین شامل یکی از این پیاده سازی ها است - کلاس RNGCryptoServiceProvider را ملاقات کنید. ایده رمزارز RNG این است که حتی اگر هنوز یک تولید کننده اعداد شبه تصادفی باشد، نتایج غیرقابل پیش بینی نسبتاً قوی را ارائه می دهد. RNGCryptoServiceProvider از چندین منبع آنتروپی استفاده می کند که اساساً "نویز" در رایانه شما هستند و پیش بینی توالی اعدادی که تولید می کند بسیار دشوار است. علاوه بر این، نویز "در کامپیوتر" نه تنها به عنوان حالت اولیه، بلکه بین تماس‌های شماره‌های تصادفی بعدی نیز قابل استفاده است. بنابراین، حتی با دانستن وضعیت فعلی کلاس، محاسبه اعداد بعدی که در آینده تولید خواهند شد و اعدادی که قبلا تولید شده اند کافی نخواهد بود. در واقع، رفتار دقیق به اجرا وابسته است. علاوه بر این، ویندوز می‌تواند از سخت‌افزار تخصصی استفاده کند که منبع «تصادفی واقعی» است (به عنوان مثال، یک حسگر فروپاشی ایزوتوپ رادیواکتیو) تا حتی اعداد تصادفی مطمئن‌تر و مطمئن‌تری تولید کند.

    بیایید این را با کلاس Random که قبلا بحث شد مقایسه کنیم. فرض کنید ده بار با Random.Next(100) تماس گرفتید و نتایج را ذخیره کردید. اگر قدرت محاسباتی کافی دارید، می توانید تنها بر اساس این نتایج، حالت اولیه (seed) را که نمونه تصادفی با آن ایجاد شده است، محاسبه کنید، نتایج بعدی فراخوانی Random.Next(100) را پیش بینی کنید، و حتی نتایج را محاسبه کنید. فراخوانی های روش قبلی اگر از اعداد تصادفی برای اهداف امنیتی، مالی و غیره استفاده می کنید، این رفتار به شدت غیرقابل قبول است. Crypto RNGها به طور قابل توجهی کندتر از کلاس Random کار می کنند، اما دنباله ای از اعداد را تولید می کنند که هر کدام از مقادیر دیگر مستقل تر و غیرقابل پیش بینی تر هستند.

    در بیشتر موارد، عملکرد ضعیف مانع از معامله نیست - یک API بد است. RandomNumberGenerator برای تولید توالی بایت طراحی شده است - این همه است. این را با متدهای کلاس Random مقایسه کنید، جایی که می توان یک عدد صحیح تصادفی، عدد کسری و همچنین مجموعه ای از بایت ها را بدست آورد. یکی دیگر از ویژگی های مفید، توانایی به دست آوردن یک عدد تصادفی در یک محدوده مشخص است. این احتمالات را با آرایه بایت های تصادفی که RandomNumberGenerator تولید می کند مقایسه کنید. می‌توانید با ایجاد wrapper (wrapper) خود در اطراف RandomNumberGenerator وضعیت را اصلاح کنید، که بایت‌های تصادفی را به یک نتیجه «مناسب» تبدیل می‌کند، اما این راه‌حل بی‌اهمیت است.

    با این حال، در بیشتر موارد، اگر بتوانید مشکلی را که در ابتدای مقاله توضیح داده شد، حل کنید، "ضعف" کلاس Random خوب است. بیایید ببینیم در اینجا چه کاری می توانیم انجام دهیم.

    از یک نمونه از کلاس Random برای چندین تماس استفاده کنید
    در اینجا، ریشه راه حل مشکل استفاده از تنها یک نمونه از Random هنگام ایجاد تعداد زیادی اعداد تصادفی با استفاده از Random.Next است. و این بسیار ساده است - ببینید چگونه می توانید کد بالا را تغییر دهید:
    // این کد بهتر خواهد بود Random rng = new Random(); برای (int i = 0; i< 100; i++) { Console.WriteLine(GenerateDigit(rng)); } ... static int GenerateDigit(Random rng) { // Предположим, что здесь много логики return rng.Next(10); }
    حالا هر تکرار اعداد متفاوتی خواهد داشت... اما این همه چیز نیست. اگر این بلوک کد را دو بار پشت سر هم فراخوانی کنیم چه اتفاقی می افتد؟ درست است، ما دو نمونه تصادفی با همان دانه ایجاد می کنیم و دو مجموعه یکسان از اعداد تصادفی به دست می آوریم. اعداد در هر مجموعه متفاوت خواهد بود، اما این مجموعه ها در بین خود برابر خواهند بود.

    دو راه برای حل مشکل وجود دارد. ابتدا می‌توانیم از یک نمونه استفاده نکنیم، بلکه از یک فیلد ثابت حاوی نمونه‌ای از Random استفاده کنیم و سپس کد بالا تنها یک نمونه ایجاد می‌کند و از آن استفاده می‌کند و هر چند بار که لازم است آن را فراخوانی می‌کند. ثانیاً، می‌توانیم ایجاد یک نمونه تصادفی را از آنجا به طور کامل حذف کنیم، و آن را "بالاتر" منتقل کنیم، در حالت ایده‌آل به همان "بالای" برنامه، جایی که یک نمونه تصادفی واحد ایجاد می‌شود، پس از آن به همه مکان‌ها منتقل می‌شود. جایی که به اعداد تصادفی نیاز است. این یک ایده عالی است که به خوبی توسط وابستگی ها بیان می شود، اما تا زمانی که فقط از یک رشته استفاده کنیم، کار خواهد کرد.

    ایمنی نخ

    کلاس Random ایمن نیست. با در نظر گرفتن اینکه چقدر دوست داریم یک نمونه واحد بسازیم و از آن در طول یک برنامه برای کل مدت اجرای آن استفاده کنیم (تک تک، سلام!)، عدم ایمنی نخ تبدیل به دردسر واقعی می شود. به هر حال، اگر از یک نمونه به طور همزمان در چندین رشته استفاده کنیم، احتمال بازنشانی حالت داخلی آن وجود دارد و اگر این اتفاق بیفتد، از آن لحظه به بعد نمونه بی‌فایده می‌شود.

    باز هم دو راه برای حل مشکل وجود دارد. مسیر اول همچنان شامل استفاده از یک نمونه واحد است، اما این بار با استفاده از قفل کردن منابع از طریق مانیتور. برای انجام این کار، باید یک پوشش در اطراف Random ایجاد کنید که تماس‌های متدهای خود را در یک عبارت قفل قرار دهد و دسترسی انحصاری تماس‌گیرنده به نمونه را تضمین کند. این مسیر بد است زیرا عملکرد را در سناریوهای فشرده با نخ کاهش می دهد.

    راه دیگری که در زیر توضیح خواهم داد، استفاده از یک نمونه در هر رشته است. تنها چیزی که باید مطمئن شویم این است که هنگام ایجاد نمونه از seed های مختلف استفاده می کنیم، بنابراین نمی توانیم از سازنده های پیش فرض استفاده کنیم. در غیر این صورت، این مسیر نسبتاً ساده است.

    ارائه دهنده ایمن

    خوشبختانه کلاس عمومی جدید ThreadLocal ، که در .NET 4 معرفی شده است، نوشتن ارائه دهندگانی را که یک نمونه در هر رشته ارائه می کنند بسیار آسان می کند. شما فقط باید یک نماینده به سازنده ThreadLocal ارسال کنید، که به دریافت مقدار خود نمونه ما اشاره دارد. در این مورد، من تصمیم گرفتم از یک مقدار seed استفاده کنم و آن را با استفاده از Environment.TickCount (که دقیقاً چگونه سازنده تصادفی بدون پارامتر کار می کند) مقداردهی اولیه کنم. در مرحله بعد، هر بار که نیاز داریم یک نمونه تصادفی جدید برای یک رشته جداگانه بدست آوریم، تعداد تیک های حاصل افزایش می یابد.

    کلاس زیر کاملا ثابت است و فقط شامل یک متد عمومی (باز) GetThreadRandom است. این متد به‌جای یک ویژگی، به‌عنوان یک متد ساخته می‌شود، عمدتاً برای راحتی: این تضمین می‌کند که همه کلاس‌هایی که به نمونه‌ای از Random نیاز دارند، به Func بستگی دارند. (نماینده ای که به متدی اشاره می کند که هیچ پارامتری نمی گیرد و مقداری از نوع Random برمی گرداند)، و نه از خود کلاس Random. اگر یک نوع در نظر گرفته شده است که روی یک رشته واحد اجرا شود، می‌تواند یک نماینده را برای بدست آوردن یک نمونه تصادفی فراخوانی کند و سپس از آن در سراسر استفاده کند. اگر نوع نیاز به کار در سناریوهای چند رشته ای داشته باشد، می تواند هر بار که به یک مولد اعداد تصادفی نیاز دارد، نماینده را صدا کند. کلاس زیر به تعداد thread ها نمونه های کلاس Random ایجاد می کند و هر نمونه از مقدار اولیه متفاوتی شروع می شود. اگر نیاز به استفاده از ارائه‌دهنده اعداد تصادفی به عنوان یک وابستگی در انواع دیگر داشته باشیم، می‌توانیم این کار را انجام دهیم: TypeThatNeedsRandom(RandomProvider.GetThreadRandom) جدید. خب، این خود کد است:
    با استفاده از سیستم؛ با استفاده از System.Threading. کلاس استاتیک عمومی RandomProvider ( int static private seed = Environment.TickCount؛ ThreadLocal استاتیک خصوصی randomWrapper = ThreadLocal جدید (() => new Random(Interlocked.Increment(ref seed))); عمومی استاتیک تصادفی GetThreadRandom() ( randomWrapper.Value; ) )
    به اندازه کافی ساده، درست است؟ این به این دلیل است که تمام کدها با هدف تولید نمونه صحیح Random هستند. هنگامی که یک نمونه ایجاد و برگردانده شد، مهم نیست که بعداً با آن چه کاری انجام می دهید: تمام موارد صدور نمونه های بعدی کاملاً مستقل از نمونه فعلی هستند. البته، کد کلاینت یک حفره برای سوء استفاده مخرب دارد: می‌تواند یک نمونه از Random را بگیرد و به جای فراخوانی RandomProvider ما در آن رشته‌های دیگر، آن را به رشته‌های دیگر ارسال کند.

    مشکلات طراحی رابط

    یک مشکل هنوز باقی است: ما از یک مولد اعداد تصادفی محافظت شده ضعیف استفاده می کنیم. همانطور که قبلا ذکر شد، نسخه بسیار امن تری از RNG در RandomNumberGenerator وجود دارد که پیاده سازی آن در کلاس RNGCryptoServiceProvider است. با این حال، API آن برای استفاده در سناریوهای استاندارد بسیار دشوار است.

    بسیار خوب است اگر ارائه دهندگان RNG در چارچوب «منابع تصادفی» جداگانه داشته باشند. در این مورد، ما می‌توانیم یک API واحد، ساده و راحت داشته باشیم که هم با اجرای ناامن اما سریع و هم با اجرای امن اما آهسته پشتیبانی می‌شود. خوب، خواب دیدن ضرری ندارد. شاید عملکرد مشابهی در نسخه های بعدی .NET Framework ظاهر شود. شاید کسی که از مایکروسافت نیست، اجرای خود را از آداپتور ارائه دهد. (متاسفانه، من آن شخص نخواهم بود... پیاده سازی درست چنین چیزی به طرز شگفت انگیزی پیچیده است.) همچنین می توانید کلاس خود را با استخراج از Random و نادیده گرفتن متدهای Sample و NextBytes ایجاد کنید، اما دقیقاً مشخص نیست که چگونه باید آنها را انجام دهند. کار، یا حتی نمونه پیاده سازی خودتان می تواند بسیار پیچیده تر از آن چیزی باشد که به نظر می رسد. شاید دفعه بعد…