برزدن فیشر یتس

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

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

نسخهٔ اولیه پیچیدگی زمانی

دارد اما نسخهٔ جدید این الگوریتم بهینه است و زمان اجرای آن ضریبی از تعداد عناصر و خطی می‌باشد.
همچنین درجا اجرا می‌شود و نیاز به حافظهٔ اضافی ندارد.

روش اصلی بر زدن فیشر یتس

نسخهٔ اولیه بر زدن فیشر-یتس در سال ۱۹۳۸ توسط رونالد فیشر و فرنک یتس در کتاب جداول آماری برای تحقیقات زیستی، زراعتی، پزشکی منتشر شد.

این الگوریتم بسیار ساده است و برای استفادهٔ مستقیم انسان‌ها مناسب می‌باشد زیرا تنها به کاغذ و خودکار نیازمند است! اما به دلیل پیچیدگی زمانی بالا توسط کامپیوترها که با دنباله‌های بزرگ سر و کار دارند به کار گرفته نمی‌شود.
در این نسخه خاصیت تصادفی توسط جدولی از اعداد تصادفی ایجاد می‌شود. روش ایجاد جایگشت تصادفی اعداد ۱ تا
n
به شرح زیر است:
۱-
اعداد یک تا
n
را بنویسید.
۲-
عدد تصادفی
k
را بین یک و تعداد اعداد خط نخورده انتخاب کنید.
(k
می‌تواند یک و تعداد اعداد باقی‌مانده نیز باشد)
۳-
k
امین عدد خط نخورده را پیدا کنید و انتهای لیستی دیگر بنویسید. سپس آن را خط بزنید.
۴-
مراحل ۲ و ۳ را تکرار کنید تا تمامی اعداد خط بخورند.
۵-
دنبالهٔ اعداد ایجاد شده در لیست، جایگشتی از دنبالهٔ اولیه است.

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

نسخهٔ جدید الگوریتم فیشر یتس

نسخهٔ جدید بر زدن فیشر یتس برای استفادهٔ کامپیوترها طراحی شده. این نسخه در سال ۱۹۶۴ توسط ریچارد داستنفلد معرفی شد
و توسط دونالد کنوت در کتاب هنر برنامه‌نویسی رایانه با عنوان «الگوریتم پی»
به شهرت رسید. به نظر می‌رسد آن‌ها از الگوریتم فیشر و یتس مطلع نبوده‌اند.

ایدهٔ نسخهٔ جدید مشابه نسخهٔ اصلی است و بر مبنای انتخاب تصادفی می‌باشد. اما تفاوت جزئی آن موجب کاهش چشم‌گیر زمان اجرا می‌شود. این الگوریتم با وجود سادگی نااریب است و از حافظهٔ اضافه استفاده نمی‌کند.

الگوریتم فشر یتس زمان غیرضروری‌ای را صرف پیدا کردن k امین عدد خط نخورده می‌کند (خط ۳). داستنفلد برای آن راه حلی ارائه کرد. در هر بار اجرای حلقهٔ دستورات عدد خط خورده را با جابه‌جا کردن آن با آخرین عدد خط نخورده به انتهای آرایه منتقل کنیم. این راه حل پیچیدگی زمانی بر زدن را از

به
 کاهش داد.
۱-
دنباله ایی به طول
n
از اعداد مورد نظر را در نظر بگیرید.
۲-
عددی تصادفی مانند

در بازهٔ
 انتخاب کنید.
k
تعداد اعدادی است که روی آن‌ها عملیات انجام شده؛ به عبارت دیگر تعداد حلقه‌های اجرا شده می‌باشد.
۳-
عنصر
ام را با عنصر
ام جابه‌جا کنید.
۴-
مراحل ۲ و ۳ را
 دفعه تکرار کنید.
۵-
دنبالهٔ شما به جایگشتی تصادفی از دنبالهٔ اولیه تبدیل شده‌است.

نمونه کد

کد زیر به زبان پایتون نوشته شده:
# Python Program to shuffle a given array
import random
# A function to generate a random permutation of arr[]
def randomize (arr, n):
 # Start from the last element and swap one by one. We don't
 # need to run for the first element that's why i > 0
 for i in range(n-1,0,-1):
 # Pick a random index from 0 to i
 j = random.randint(0,i+1)
 # Swap arr[i] with the element at random index
 arr[i],arr[j] = arr[j],arr[i]
 return arr
# Driver program to test above function.
arr = [1, 2, 3, 4, 5, 6, 7, 8]
n = len(arr)
print(randomize(arr, n))
# This code is contributed by Pratik Chhajer
خروجی کد:
7 8 4 6 3 1 2 5

مولدهای اعداد تصادفی سخت‌افزاری

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

مهاجم‌ها با یافتن این بیت‌ها می‌توانند به ما آسیب بزنند. برزدن بیت‌های عدد تولید شده می‌تواند این مشکل را بر طرف کند زیرا هربار بیت‌های همبسته به مکان‌های متفاوتی می‌روند و مهاجم نمی‌تواند آن‌ها را شناسایی کند. البته بر زدن یکی از مشکلات عمدهٔ مولدهای اعداد تصادفی سخت‌افزاری که اریب بودن آنهاست را حل نمی‌کند. این مولدها بیت صفر را به احتمال ۰٫۵ تولید نمی‌کنند و با برزدن تعداد صفر و یک‌ها تغییر نمی‌کند و مولد همچنان اریب باقی می‌ماند.

مرتب‌سازی ساختگی

کلیت الگوریتم به هم ریختن تصادفی آرایه و تولید جایگشت‌های تصادفی تا رسیدن به آرایهٔ مرتب‌شده‌است. این الگوریتم بهینه نیست و در بدترین حالت می‌تواند به اتمام نرسد و زمان بی‌نهایت داشته‌باشد.

تولید عدد تصادفی در بازهٔ دلخواه

علاوه بر توجه به الگوریتم فیشر یتس باید به الگوریتم به کارگرفته‌شده برای تولید عدد تصادفی در مرحلهٔ دو نیز توجه کنیم. اریب بودن الگوریتم مذکور موجب اریب شدن جایگشت نهایی می‌شود.
معمولاً مولدهای اعداد تصادفی، اعدادی در بازهٔ
[

,
۰]
تولید می‌کنند. یکی از روش‌های معمول برای پیدا کردن عدد تصادفی در بازهٔ
[

,
۱]
استفاده از روش باقی‌مانده می‌باشد. (

می‌تواند تعداد اعداد خط نخورده در روش اصلی فیشر یتس یا تعداد حلقه‌های باقی‌مانده در نسخه جدید باشد)
این روش در حالت کلی مناسب نمی‌باشد و نتیجهٔ نهایی ما را اریب می‌کند.

فرض کنید مولد، عدد تصادفی صحیح
r
را در بازهٔ
[

,
۰]
تولید کند. روش باقی‌مانده

را به عنوان عدد تصادفی در بازهٔ
[

,
۱]
خروجی می‌دهد. (
 باقی‌ماندهٔ تقسیم  بر  می‌باشد)

به‌طور مثال فرض کنید مولد، عددی در بازهٔ
[۹۹
,
۰]
تولید کند و ما عددی در بازهٔ
[۱۵
,
۱]
بخواهیم. اگر اعداد صحیح بازهٔ
[۹۹
,
۰]
را بر ۱۵ تقسیم و باقی‌مانده را با یک جمع کنیم، اعداد یک تا ده، ۷ مرتبه و سایر اعداد ۶ مرتبه ظاهر می‌شوند؛ زیرا ۱۰۰ بر ۱۵ بخش‌پذیر نیست. پس توزیع اعداد تصادفی تولید شده در بازهٔ
[۱۵
,
۱]
یکنواخت نیست و اریب می‌باشد.