شاید برای شما هم پیش آمده باشد که برای ساخت یک بازی یا الگوریتم رمزگذاری یا هر منظور دیگری نیاز به ساخت اعداد تصادفی داشته باشید! ممکن است فکر کنید که ساخت یک عدد تصادفی کار زیاد پیچیدهای نیست و با صدازدن یک تابع Rand یا تابعی مشابه آن، کار تمام میشود. اما عدد تولیدشده با این روش چقدر تصادفی است؟ آیا نتیجه قابل تکرار نیست؟
در این مقاله قصد داریم این موضوع را مورد کنکاش قرار دهیم. پس با سیسوگ همراه شوید.
فرقی نمیکند که از کدام زبان برای برنامهنویسی استفاده میکنید؛ معمولاً برای ساخت اعداد تصادفی از فانکشنی استفاده میشود که اعداد تصادفی ایجاد میکند. برای جلوگیری از پیچیدگی و اینکه نتایج قابل تست باشد، از زبان C بهعنوان زبان مرجع استفاده میکنیم و مثالها را به زبان C خواهیم نوشت. در زبان C تابعی به نام rand وجود دارد که اعداد تصادفی میسازد. اگر بخواهیم بهدقت به موضوع نگاه کنیم، احتمالاً پشت این تابع، یک الگوریتم برای ساخت اعداد تصادفی است که چون پارامتر متغیری در آن وجود ندارد، به ازا هر بار اجرا، یک نتیجهی مشخص را بسته به الگوریتم تعریفشده بر خواهد گرداند.
برای روشنتر شدن مسئله فوق به مثال زیر توجه کنید:
1 2 3 4 5 6 | int main(void) { int i = 0; for(i=0;i<10;i++) printf("%i\t",rand()); } |
در برنامه فوق ما قصد داشتیم که 10 عدد تصادفی ایجاد کنیم و نتیجه را نمایش دهیم. برای این کار از یک حلقه استفاده میکنیم که 10 بار تابع rand را فراخوانی کند و نتیجه را نمایش دهد. به نظر توالی اعداد تولیدشده اتفاقی هستند.
البته در نظر داشته باشید این توالی تا وقتی اتفاقی است که ما از الگوریتم تولید آنها اطلاع نداشته باشیم. وقتی الگوریتم را مطالعه کنیم و روش عملکرد آن را دربیابیم با داشتن یکی از اعداد میتوانیم عدد بعدی و حتی قبلی را حدس بزنیم! در این مرحله این مسئله را نادیده میگیریم.
1 | 41 18467 6334 26500 19169 15724 11478 29358 26962 24464 |
انتظار داریم که با هر بار اجرای برنامه ده عدد کاملاً اتفاقی داشته باشیم؛ اما هر بار که برنامه را اجرا میکنیم، همین ده عدد نمایش داده میشوند. (ممکن است برای شما ده عدد دیگر باشد ولی نتیجه تکرارپذیر است.) یعنی اعداد واقعاً تصادفی نیستند و توسط الگوریتمی ایجاده شدهاند که هیچ پارامتر متغیری نداشته است.
برای رفع این نقص، تابعی وجود دارد به نام srand که با دریافت یک عدد، اعداد تصادفی را از آن نقطه شروع خواهد کرد. به این معنی که اگر در هر بار اجرای برنامه، ما یک عدد تصادفی (که خود ساخت این عدد نیز مسئلهساز است.) به این تابع بدهیم، خروجی نیز اعداد تصادفی است!
ازآنجاییکه در شروع برنامه اعداد تصادفیای در کار نیست، ما برای اینکه هر بار اجرای برنامه نتیجهای متفاوت در بر داشته باشد، زمان را بهعنوان ورودی این تابع در نظر میگیریم. این کار به نظر منطقی میرسد. هر بار که برنامه را اجرا میکنیم، با توجه به زمان اجرای برنامه، نتیجهای متفاوت خواهیم داشت.
1 2 3 4 5 6 7 8 | int main(void) { int i = 0; srand ( time(NULL) ); for(i=0;i<10;i++) printf("%i\t",rand()); return 0; } |
با هر بار اجرای برنامه، ما نتایجی متفاوت از حالت قبل دریافت خواهیم کرد. به نسبت روش قبل، نتیجه مقداری بهبود پیدا کرده ولی باز مشکل بهصورت کامل حل نشده است! چراکه اگر برنامه در بازهای کوتاهتر از یک ثانیه اجرا شود، نتایجی مشابه خواهد داد و همچنین باز اعداد تولیدشده آنچنان تصادفی نیستند، فقط مقداری محاسبه آنها سخت شده است.
فرض کنید ما سیستم تولید رمز عبور تصادفی را با این روش پایهگذاری کردهایم؛ یک هکر با دانستن زمان ایجاد رمزها، اگر زمان سیستم خود را به گذشته تغییر دهد (یعنی دقیقاً همان زمانی که ما رمز عبورها را ایجاد کردهایم) خواهد توانست رمزهای مشابه ایجاد کند و حتی با دستکاری زمان سیستم، رمزهایی که در آینده ایجاد میکنیم را نیز حدس بزند.
همانگونه که مشاهده میکنید برخلاف آن چیزی که تاکنون فکر میکردهاید، اعداد تولیدشده آنچنان هم تصادفی نبودهاند و بهراحتی قابل تکرار و حدس هستند؛ البته نه آنقدر راحت که برای همه مقدور باشد.
معمولاً در سیستمهای مبتنی بر میکروکنترلر، زمان واقعی (RTC) وجود ندارد و زمان سیستم معمولاً از زمان روشن شدن دستگاه سنجیده میشود. خود این مسئله باعث میشود که عملاً تابع srand بیاستفاده شود؛ چراکه مثلاً همیشه در زمان خاصی از اجرای برنامه فراخوانی میشود و همیشه یک مقدار خاص به آن داده میشود. برای روشن شدن مسئله، برنامه بالا را برای سختافزار آردوینو بازنویسی میکنیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 | void setup() { // put your setup code here, to run once: Serial.begin(9600); srand(millis()); for(int i=0;i<10;i++) Serial.println(rand()); } void loop() { // put your main code here, to run repeatedly: } |
همانطور که مشاهده میکنید با هر بار اجرای برنامه (ریست کردن یا خاموش و روشن کردن) برنامه خروجی یکسانی را نمایش میدهد. البته لازم به ذکر است که اگر در سیستم از RTC استفاده شود، مقداری وضع بهبود پیدا میکند و حدس زدن خروجی سختتر خواهد شد. اما باز به معنی واقعیِ کلمه، اعداد تولیدشده تصادفی نخواهند بود.
چراکه برای تصادفی بودن یک دنباله از اعداد، باید دو شرط وجود داشته باشد؛ اول: دنباله تکرارپذیر نباشد و دوم: اعداد تولیدشده قابل حدس زدن نباشند.
ااما راهحل اساسی برای تولید اعداد واقعاً تصادفی چیست؟
همانطور که گفته شد اعداد تصادفی وقتی به مفهوم واقعی خود نزدیکتر میشوند که غیرقابلپیشبینی و تکرارناپذیر باشند. اگر پایه تولید اعداد تصادفی را میزان تجمع نوع خاصی از باکتری بر روی یک سطح انتخاب کنیم، اعداد تولیدشده با این روش بهراحتی قابل پیشبینی نیستند؛ چراکه عوامل متغیر زیادی در میزان رشد آن نوع خاص از باکتری وجود خواهد داشت. بهعنوانمثال: دمای هوا، میزان نور، میزان رطوبت، میزان وجود نوع دیگری از باکتری و… . به همین دلیل حدس اینکه در یک زمان خاص چه مقدار باکتری بر روی سطح موردنظر ما تجمع کرده است عملاً امری غیرممکن خواهد بود و میتوان تا حدود زیادی گفت که عدد بهدستآمده عددی است تصادفی! نمونه فوق فقط یک مثال برای نشان دادن این مسئله بود که با زیاد کردن پارامترهای دخیل در تولید یک عدد تصادفی، میتوان به همان نسبت حدس زدن عدد تولیدشده را دشوارتر کرد.
روشهای متفاوتی برای تولید اعداد تصادفی با استفاده از اصول ذکرشده وجود دارد. اما روشهایی که چندان پیچیده نباشند و بتوان بهراحتی آنها را پیادهسازی کرد محدود هستند. ما در این مقاله قصد داریم تا برای تولید اعداد تصادفی از نویز سفید (White Noise) استفاده کنیم. از نظر فیزیکدانان نویز سفید سیگنالی تصادفی است که تراکم طیفی ثابتی دارد.
اگر بخواهیم به شکل سادهتر مسئله را بیان کنیم باید بگوییم که مثلاً گوش ما انسانها، معمولاً محدودهی فرکانس ۲۰ هرتز تا ۲۰ هزار هرتز را میشنود. اگر یک دستگاه صوتی بخواهد نویز سفید برای گوش ما تولید کند، باید توان امواجی که از آن در محدوده فرکانسی ۲۰ تا ۴۰ هرتز ساطع میشود، با توان امواجی که مثلاً در محدوده فرکانسی ۱۰۰۰۰ و ۱۰۰۲۰ ساطع میشود، برابر باشد.
صدای نویز سفید را بشنوید:
صدای اقیانوس نیز نویز سفید است:
یکی از سادهترین روشهای تولید نویز سفید، استفاده از دیود بهصورت بایاس معکوس هست. همانطور که میدانید دیود از پیوند N و P تشکیل شده است. وقتیکه دیود را بهصورت معکوس در مدار قرار میدهیم، پیوند PN گسترش مییابد و میزان جریان کمی برقرار میشود که به این جریان، جریان نشتی میگویند. این جریان با توجه به ماهیت دیود و پیوند PN دارای نویز سفید است.
بر همین اساس برای تولید نویز سفید میتوان از مدار زیر استفاده کرد:
اگر خروجی مدار فوق را به یک تقویتکننده صوتی وصل کنیم، صدای نویز سفید را خواهیم شنید. برای تقویت دامنه و تصحیح امپدانس مدار میتوان خروجی مدار را به یک تقویتکننده عملیاتی (Opamp) وصل کرد.
مدار نهایی به شکل زیر خواهد بود:
میتوان خروجی مدار فوق را به ورودی آنالوگ میکروکنترلر و یا حتی به کارت صوتی کامپیوتر وصل کرد! نمونههای بهدستآمده از این روش کاملاً تصادفی هستند و بههیچوجه نمیتوان نتیجه اعداد تولیدشده را تکرار کرد.
در برنامه زیر برای ایجاد یک عدد تصادفی از نویز سفید و ADC به کمک توابع Rand و Srand استفاده شده است.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int adc_pin = 0; void setup(){ Serial.begin(9600); } void loop() { int adc_value = analogRead(adc_pin); srand(adc_value); Serial.println(rand()); delay(1000); } |
خروجیِ دیجیتالِ مدارِ رندم ژنراتور را برای مدت 15 ساعت با نرخ نمونهبرداری 500 میکروثانیه ذخیره و موردبررسی قراردادیم که نتایج زیر بهدست آمد:
اگر هر بیت را یک پیکسل فرض کنیم و دادههای ذخیرهشده را بهصورت یک عکس آشکار کنیم، تصویر زیر بهدست خواهد آمد که شباهت زیادی به تصویر برفکی تلویزیونها دارد.
نمودار پراکندگی دادههای موجود:
882000 نمونه اول را به صورت فایل صوتی بشنوید:
سلام
وقت بخیر
این مقاله رو حدود یک سال قبل خونده بودم. عالی بود
اما حالا یه چیزی ذهن منو مشغول کرده.
این که مگه انرژی هر الکترون جذب یا نشر می کند کوانتومی نیست؟
با این فرض میشه گفت هر انرژی و به هر شکلی که بین تمام ذرات منتقل میشه مضرب صحیحی از مقدار بسته های انرژی هست و عملا چیز رندمی و بینا بینی وجورد نداره. اما چی میشه که اتفاقات رندم زیادی دور و بر ما وجود داره مثل نویز و حرکت ابر و ….
میشه گفت به خاطر اینکه عوامل زیادی توی این مدل اتفاقات دخیل هست، رندم هستن؟
اگه این طور باشه نمی شه خونه های یک حافظه بزرگ رو طوری به هم مرتبط کنیم که بشه یه عدد رندم تولید کرد؟
(ببخشید اگه اشکال علمی تو سوالم هست. بیش تر از این از یه دبیرستانی برنمیاد!)
سلام دوست عزیز
در واقع سوال شما مقداری فلسفی است
به نظر اگر ما از اتفاقات پیرامون خودمان به اندازه کافی آگاهی داشته باشیم،آن وقت دیگه بحث رندمی در کار نخواهد بود، و دقیقا مساله اینه که ما آگاهی کافی رو در خصوص ابعاد یک اتفاق نداریم و به همین دلیل به نظر ما اتفاق رندم می آید!
در این لحظه مثال الکترونیکی مربوطی پیدا نکردم ولی اگه بخوام اهمیت آگاهی رو روشن کنم میتونم گسترش بیماری سل رو در اروپا مثال بزنم، تا قبل از این که کشف بشه ریشه آن باکتری است و از طریق آب منتقل میشه غالبا فکر می کردند نفرین است یا طلسم است یا کار شیاطین است و …. تا این که آگاهی به دست آمد!
در خصوص اتفاقات رندم هم من فکر میکنم به همی شکله – اگر ما از همه اتفاقات کیهان آگاه باشیم آن گاه هیچ اتفاقی برای ما رندم و غافل گیرکننده نیست
و مساله اینه که ما این آگاهی رو نداریم حداقل فعلا
درست میگی ولی راهی نیست تا با کد این کار رو بکنیم
هر کدی قابل پیشبینی است حداقل برای نویسنده اون کد !
ساده ترین کار روشن کردن adc است و خواندن یکی از کانال هایی است که به جایی وصل نشده
فکر کنم بشه از پینهای شناور میکرو هم برای این منظور استفاده کرد. یه نرم افزار درست کرده بودم که مقادیر خام پینها و کانالهای ADC رو با USB از میکرو میگیره و نمایش میده. متوجه شدم مقدار ADC روی پینهایی که به ولتاژ مشخصی وصل نیستن (شناور) به طور مداوم و تصادفی در حال تغییره. یا مقدار یه پین tri-state شناور مدام بین ۰ و ۱ تغییر میکنه. به نظر شما روشهای خوبی هستن؟
البته فکر کنم یه نقص این روش اینه که یه میدان الکترومغناطیسی نسبتا قوی میتونه پین رو روی یه حالت ثابت نگه داره. یه نقص دیگه هم اینه که اگه پینهای روی حالت tri-state رو pull-up یا pull-down نکنیم جریان مصرفی میکرو بالا میره.
اینم میشه روش خوبیه ولی تو محیطی که نویز کم باشه دامنه تغییرات زیاد نیست و مشکل ساز میشه
مشکل اصلی اینه که میشه هکش کرد – چطور؟ خوب با ساختن نویز های هدفمند 🙂
سلام، ممنون از مطالب مفیدتون.
میشه برای اتصال به میکرو فقط از همون مدار ابتدایی (فقط مدار 2 ترانزیستوری) استفاده کرد و دیگه آپ امپ و تقویت کننده رو بهش وصل نکرد؟
شدنش میشه ولی باید دامنه تغییرات رو در بازه کاری میکروکنترلر تنظیم کنید!! مثلا ببینید بیشترین ولتاژی که میگیرید از مدار چقدر و کمترین چقدره – این بازه رو بین ۰ تا مثلا ۳٫۳ مقیاس کنید و بدید به adc میکرو 🙂
ببخشید یادم رفت بپرسم .
اون نمودار پراکندگی محورهاش چه کمیتی هست و اون نقطه های سبز و قرمز چه چیزی رو بیان میکنه ؟
با سپاس فراوان .
میتونید یه ماژول میکروفون بزارید حساسیتش ببرید بالا برا ورودی اطلاعات
امم باید سورس مطلبش رو ببینم
متاسفانه الان حضور ذهن ندارم
باید نگاه کنم بگم خدمتتون
سپاس بابت وقتی که میگذارید.
خواهش میکنم
سلام .
بسیار مقاله جالبی بود .
در یک مستند تلوزیونی دیدم از حالت ملوکولهای بلور به عنوان رمز عبور استفاده کرده بود .یک اسلاید با ابعاد مشخص از یک ماده بلوری را به عنوان یک کلید غیر قابل هک استفاده میکرد . خیلی جالب بود .
برای من چند تا سوال پیش اومد .
اون قسمت analog output به مبدل adc میکرو میره ؟.
اما در مورد digital output :
شماتیک نشون میده به خاطر op amp آخری ، خروجی بین 0 و 1 نوسان میکنه .حالت وسط نداره . سازوکارش اینطوریه که مثلا 8 با ر خروجی رو میخونیم و یک عدد یک عدد 8 بیتی به ما میده .?
به نظرم بشه از نویز های رادیویی هم همچین استفاده ای کرد .
سلام دوست عزیز
بله واقعا هر روز مساله امنیت داره پیشرفت میکنه
خروجی مدار رو باید به ورودی adc وصل کنید.
امم پیشنهاد خوبیه ولی فکر میکنم نرخ نمونه برداری کاهش پیدا میکنه – تنها مساله اینه در واقع
سپاس .
ولی نقش اون digital output رو متوجه نشدم.
شما این خروجی رو میتونید به عنوان ورودی یه تایمر استفاده کنید و بعد عدد تایمر رو توی تابع srand استفاده کنید ! به همین سادگی 🙂
برای زمان هایی که ورودی آنالوگ در دسترس شما نباشه کاربرد داره!
پس کارکردش اینطوریه …
سپاس لطف کردید.
بله
خواهش میکنم
با استفاده از ماتریس نمیشه عدد تولید کرد ؟
سلام
منظورتون چطور استفاده ای از ماتریس هست ؟!
سلام یک ماتریس با ابعاد کوچک و در 10 لیست تعریف کنیم و سپس بخوایم هر کدوم از ماتریس ها نمایش پیدا کنه و عدد تولید بشه آیا اصلا چنین امکانی وجود داره مثلا
Matrixrand = [ [1,2,3,4,5] [2,0,4,5] [3,4,1,0]]
امم خوب شدنش میشه ولی میدونی جایگزینی این روال با یه فرمول ریاضی یا هر چیزی دیگه ای کمک میکنه تا حافظه کمتری از سیستم گرفته بشه
که خوب توی سیستم های میکروکنترلری خیلی مساله مهمی هستش
بابا تو دیگه کی هستی!
^_^
سلام بسیارعالی بود
خواهش میکنم
سلام سپاسگزارم عالی بود
یک سوال من متوجه شدم میکرو کنترلر های STM32 یک قابلیت برای ایجاد اعداد تصادفی واقعی دارند شما راجب اون اطلاعی دارید ؟
سپاس
سلام خواهش میکنم دوست عزیز
بله درست متوجه شدید ، توی سری F4 به بعد واحد RNG یا Random Number Generator اضافه شده که دقیقا چنین کاری رو انجام میده !
کار کردن باهاش خیلی ساده است اگر شد انشالله بررسیش خواهیم کرد.
یک پیشنهاد اینه که به جای استفاده امدار پیچیده نویز سفید از یک سنسور دما استفاده کرد یا برخی میکرو کنترلر ها خودشون دارای سنسور دما هستند و یا میشه یک سنسور دمای ساده رو بدون هیچ فیلتر و خازن بر روی تغذیش استفاده کرد با کمی تغییر در مغدار adc میشه اعداد رندم ساخت
پیشنهاد خوبیه ! و اغلب مواقع فابل استفاده و البته هوشمندانه است ، ولی همیشه بیم این میره که تو شرایط دمایی یکسان نتایج تکرار شوند !
فکر میکنم اگر فلسفه مساله رو مد نظر قرار بدیم ، یعنی حتی نتونیم شرایطی ایجاد کنیم که نتایج تکرار شوند. ول
تشکر از مقاله شما
در برخی از مایکروکنترلرهای نسل جدید، یک واحد سخت افزاری برای تولید اعداد رندم حقیقی پیشبینی شده است که کار رو بسیار ساده میکند.
بله کاملا درسته ؛ به عنوان نمونه خانواده STM32F4 به بعد داری چنین قابلیتی هستند.
سلام
من برای ساختن یک تاس الکترونیکی از میکروکنترلر استفاده کردم
برای تولید اعداد 1 تا 6 ساده ترین راه را انتخاب کردم
بین یک DO LOOP هر بار نتغیر را مساوی با یکی از این اعداد قرار دادم و برای غیرقابل حدس زدن کردنش . ترتیب چیدمان اعداد را بهم ریختم . وبعلاوه هر کدام را بصورت تصادفی دوبار تکرار کردم مثلا اینطوری n=1 n=6 n=3 n=5 n=6 n=3 n=2 n=4 n=1
بین هر یک هم یک وقفه گذاشتم . و دستورif switch=1 then goto show
به نظر خودم این تاس خیلی خوب و اعداد تصادفی تولید میکنه .
نظر شما چیه؟
به نظرم از زمان فشردن سوئیچ که توسط یک عامل خارجی مثل انسان ایجاد میشه برای غیر قابل پیش بینی شدنش استفاده کردید درسته ؟
بله کاملا درسته ؛ استفاده از عومل خارجی تاثیر زیادی در غیر قابل پیش بینی شدن خواهند داشت.
سلام دوست عزیز روش جالبی به کار بردید ولی تا برنامه رو نبینم نمیشه گفت که واقعا اینطور هست یا خیر 🙂
باسلام و تشکر از توضیحات عالی اقای زئوس
در یک برنامه یک بازی شانس وجود داره باعنوان صفر تا 36 و البته عدد زوج و فرد …
بدین ترتیب ک ابتدا کاربر یک عدد مثلا 3رو انتخاب میکنه بعد برنامه عدد تصادفی رو میگه اگ همون 3بود ک برد اعلام میکنه و امتیاز میده اگ نه ک هیچ….
حالا میخاستم بدونم ک راهی برای حدس اعداد تصادفی ک تولید میکنه وجود داره؟چطور میشه الگوریتم رو تشخیص داد باتشکر..
باتوجه به مقاله ظاهرا هیچ وقت نمیشه عدد تصادفی واقعی تولید کرد فقط حدس زدن ورودی رو مشکل تر میشه کرد
سلام دوست عزیز ، قطعا غیر ممکن وجود ندارد ، ولی پیدا کردن الگوی بکار رفته اصلا کار ساده ای !
با فرض این که دستگاه موصوف از موتور تولید اعداد تصافی استفاده نکرده باشد (آنچه در این مقاله توضیح داده شد) ، با چند روش میتوان الگوریتم را پیدا کرد ؛ البته کار ساده ای نیست ، شما حداقل نیاز دارید که بدونید دستگاه چه مدته که روشنه و داره کار میکنه و البته این که تا حالا چند عدد تصادفی ایجاد کرده ، بعد از اون میتونید حدس بزنید که الگوریتم چی هست اونم با استفاده از سلسله اعدادی که تولید میکنه :/
و اما راه ساده تر وقتی دستگاه روشن شده و هیچ اعداد تصافیی تولید نکرده ، شما 5 بار بازی کنید ، و اعداد تولیدی رو نگاه کنید ، مثلا 1و7و15و28و33 ، دستگاه رو خاموش کنید روشن کنید باز 5 بار بازی کنید اگر همون اعداد رو تولید کرد که خوب معلوم میشه از موتور تولید اعداد تصافی استفاده نمیکنه و میتونید سلسله اعدادی که تولید میکنه رو یاداشت کنید و برای دفعه بعد ارشون استفاده کنید 🙂
عالی بود. من قبلا به این مشکل برخورده بودم. جالب اینجا بود که نتیجه تابع RND در بسکام ، توی شبیه سازی پروتئوس با نتیجه عملی یکسان بود.
من دقیقا یک حلقه For با 10 بار تکرار درست کرده بودم و انتظار داشتم با هر بار بالا اومدن میکرو یک سری اعداد متفاوت تحویل بگیرم. اما اعداد همیشه تکراری بودن.
متشکرم
بله این مساله به دلایل توضیح داده شده در مقاله ، در تمام کامپایلر ها صادق هست.
نویسنده شو !
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.