برزدن فیشر یتس
بر زدن فیشر یتس الگوریتمی برای ایجاد جایگشت تصادفی از یک دنباله است.
فرض کنید به ما آرایه‌ای داده شده‌است و می‌خواهیم جایگشتی تصادفی از اعضای آن آرایه به دست آوریم. این کار مانند بر زدن دسته‌ای ورق است و بر زدن آرایه به این معناست که هر کدام از جایگشت‌های ممکن، با احتمال مساوی بیایند و جایگشتی نااریب ایجاد شود.
نسخهٔ اولیه پیچیدگی زمانی formula_1 دارد اما نسخهٔ جدید این الگوریتم بهینه است و زمان اجرای آن ضریبی از تعداد عناصر و خطی می‌باشد. همچنین درجا اجرا می‌شود و نیاز به حافظهٔ اضافی ندارد.
نسخهٔ اولیه بر زدن فیشر-یتس در سال ۱۹۳۸ توسط رونالد فیشر و فرنک یتس در کتاب جداول آماری برای تحقیقات زیستی، زراعتی، پزشکی منتشر شد.
این الگوریتم بسیار ساده است و برای استفادهٔ مستقیم انسان‌ها مناسب می‌باشد زیرا تنها به کاغذ و خودکار نیازمند است! اما به دلیل پیچیدگی زمانی بالا توسط کامپیوترها که با دنباله‌های بزرگ سر و کار دارند به کار گرفته نمی‌شود.
در این نسخه خاصیت تصادفی توسط جدولی از اعداد تصادفی ایجاد می‌شود. روش ایجاد جایگشت تصادفی اعداد ۱ تا n به شرح زیر است:
۱- اعداد یک تا n را بنویسید.
۲- عدد تصادفی k را بین یک و تعداد اعداد خط نخورده انتخاب کنید. (k می‌تواند یک و تعداد اعداد باقی‌مانده نیز باشد)
۳- k امین عدد خط نخورده را پیدا کنید و انتهای لیستی دیگر بنویسید. سپس آن را خط بزنید.
۴- مراحل ۲ و ۳ را تکرار کنید تا تمامی اعداد خط بخورند.
۵- دنبالهٔ اعداد ایجاد شده در لیست، جایگشتی از دنبالهٔ اولیه است.
با فرض اینکه عدد تصادفی انتخاب شده در مرحلهٔ ۲ واقعاً تصادفی و نااریب باشد جایگشت ایجاد شده نیز دارای این خواص است. همچنین فیشر و یتس دربارهٔ چگونگی تولید عدد تصادفی در هر بازه‌ای توسط جداول از پیش مهیا شده توضیح دادند. این روش نااریب است.
فرض کنید می‌خواهیم جایگشتی از اعداد ۱ تا ۵ را به دست آوریم. آن‌ها را روی تکه کاغذی می‌نویسیم:
فرض کنید عدد تصادفی به دست آمده در مرحلهٔ دو ۳ باشد. با خط زدن ۳ و افزودن به لیست نهایی به نتیجهٔ زیر می‌رسیم:
به همین ترتیب ادامه می‌دهیم تا تمامی اعداد خط بخورند.
نسخهٔ جدید بر زدن فیشر یتس برای استفادهٔ کامپیوترها طراحی شده. این نسخه در سال ۱۹۶۴ توسط ریچارد داستنفلد معرفی شد و توسط دونالد کنوت در کتاب هنر برنامه‌نویسی رایانه با عنوان «الگوریتم پی» به شهرت رسید. به نظر می‌رسد آن‌ها از الگوریتم فیشر و یتس مطلع نبوده‌اند.
ایدهٔ نسخهٔ جدید مشابه نسخهٔ اصلی است و بر مبنای انتخاب تصادفی می‌باشد. اما تفاوت جزئی آن موجب کاهش چشم‌گیر زمان اجرا می‌شود. این الگوریتم با وجود سادگی نااریب است و از حافظهٔ اضافه استفاده نمی‌کند.
الگوریتم فشر یتس زمان غیرضروری‌ای را صرف پیدا کردن k امین عدد خط نخورده می‌کند (خط ۳). داستنفلد برای آن راه حلی ارائه کرد. در هر بار اجرای حلقهٔ دستورات عدد خط خورده را با جابه‌جا کردن آن با آخرین عدد خط نخورده به انتهای آرایه منتقل کنیم. این راه حل پیچیدگی زمانی بر زدن را از formula_1 به formula_3 کاهش داد.
۱- دنباله ایی به طول n از اعداد مورد نظر را در نظر بگیرید.
۲- عددی تصادفی مانند formula_4 در بازهٔ formula_5 انتخاب کنید. k تعداد اعدادی است که روی آن‌ها عملیات انجام شده؛ به عبارت دیگر تعداد حلقه‌های اجرا شده می‌باشد.
۳- عنصر formula_4ام را با عنصر formula_7ام جابه‌جا کنید.
۴- مراحل ۲ و ۳ را formula_8 دفعه تکرار کنید.
۵- دنبالهٔ شما به جایگشتی تصادفی از دنبالهٔ اولیه تبدیل شده‌است.
فرض کنید دنبالهٔ مورد نظر ۵عضوی باشد؛ مثلاً formula_9
کد زیر به زبان پایتون نوشته شده:
خروجی کد:
معمولاً بین بیت‌های اعداد تولیدشده توسط مولدهای اعداد تصادفی که به‌طور سخت‌افزاری پیاده‌سازی شده‌اند، همبستگی وجود دارد.
مهاجم‌ها با یافتن این بیت‌ها می‌توانند به ما آسیب بزنند. برزدن بیت‌های عدد تولید شده می‌تواند این مشکل را بر طرف کند زیرا هربار بیت‌های همبسته به مکان‌های متفاوتی می‌روند و مهاجم نمی‌تواند آن‌ها را شناسایی کند. البته بر زدن یکی از مشکلات عمدهٔ که اریب بودن آنهاست را حل نمی‌کند. این مولدها بیت صفر را به احتمال ۰٫۵ تولید نمی‌کنند و با برزدن تعداد صفر و یک‌ها تغییر نمی‌کند و مولد همچنان اریب باقی می‌ماند.
کلیت الگوریتم به هم ریختن تصادفی آرایه و تولید جایگشت‌های تصادفی تا رسیدن به آرایهٔ مرتب‌شده‌است. این الگوریتم بهینه نیست و در بدترین حالت می‌تواند به اتمام نرسد و زمان بی‌نهایت داشته‌باشد.
علاوه بر توجه به الگوریتم فیشر یتس باید به الگوریتم به کارگرفته‌شده برای تولید عدد تصادفی در مرحلهٔ دو نیز توجه کنیم. اریب بودن الگوریتم مذکور موجب اریب شدن جایگشت نهایی می‌شود. معمولاً مولدهای اعداد تصادفی، اعدادی در بازهٔ formula_10 ۰] تولید می‌کنند. یکی از روش‌های معمول برای پیدا کردن عدد تصادفی در بازهٔ formula_11 ۱] استفاده از روش باقی‌مانده می‌باشد. ( formula_11 می‌تواند تعداد اعداد خط نخورده در روش اصلی فیشر یتس یا تعداد حلقه‌های باقی‌مانده در نسخه جدید باشد) این روش در حالت کلی مناسب نمی‌باشد و نتیجهٔ نهایی ما را اریب می‌کند.
فرض کنید مولد، عدد تصادفی صحیح r را در بازهٔ formula_10 ۰] تولید کند. روش باقی‌مانده formula_14 را به عنوان عدد تصادفی در بازهٔ formula_11 ۱] خروجی می‌دهد. ( formula_16 باقی‌ماندهٔ تقسیم formula_17 بر formula_11 می‌باشد)
به‌طور مثال فرض کنید مولد، عددی در بازهٔ [۹۹ ۰] تولید کند و ما عددی در بازهٔ [۱۵ ۱] بخواهیم. اگر اعداد صحیح بازهٔ [۹۹ ۰] را بر ۱۵ تقسیم و باقی‌مانده را با یک جمع کنیم، اعداد یک تا ده، ۷ مرتبه و سایر اعداد ۶ مرتبه ظاهر می‌شوند؛ زیرا ۱۰۰ بر ۱۵ بخش‌پذیر نیست. پس توزیع اعداد تصادفی تولید شده در بازهٔ [۱۵ ۱] یکنواخت نیست و اریب می‌باشد.