Метод генерации случайной перестановки, алгоритм Фишера-Йетса — различия между версиями
|  (→Решение) | |||
| Строка 1: | Строка 1: | ||
| − | '''Тасование  | + | '''Тасование Фишера – Йетса''' (названо в честь Рональда Фишера (Ronald Fisher) и Франка Йетса (Frank Yates)) {{---}} алгоритм создания случайных перестановок конечного множества, попросту говоря, для случайного тасования множества.    | 
| − | Основная процедура тасования  | + | Основная процедура тасования Фишера – Йетса аналогична случайному вытаскиванию записок с числами из шляпы или карт из колоды, один элемент за другим, пока элементы не кончатся. Алгоритм обеспечивает эффективный и строгий метод таких операций, гарантирующий несмещённый результат. Время работы алгоритма <tex> O(n)</tex> | 
| ==Постановка задачи== | ==Постановка задачи== | ||
| Строка 9: | Строка 9: | ||
| Следующий алгоритм решает задачу: | Следующий алгоритм решает задачу: | ||
| − |    '''int[]''' randomPermutation('''int[] a''')  <font color = green> //   '''n''' - длина перестановки </font> | + |    '''int[]''' randomPermutation('''int[] a''')  <font color = green> //   '''n''' {{---}} длина перестановки </font> | 
|      '''for''' i = n  '''downto''' 1 |      '''for''' i = n  '''downto''' 1 | ||
|        j = random(1..i) |        j = random(1..i) | ||
| Строка 16: | Строка 16: | ||
| ==Обоснование== | ==Обоснование== | ||
| − | Корректность данного алгоритма очевидна. На каждой итерации цикла мы выбираем случайный элемент из всех оставшихся, то есть у нас есть <tex> n</tex> способов выбрать 1 элемент, <tex> n - 1</tex> способов выбрать 2 элемент ... <tex> 1</tex> способ выбрать последний элемент. Таким образом, последовательность длины <tex> n</tex> мы можем получить  <tex> $$n \times (n - 1) \times \ldots \times 1 = n! $$ </tex> способами, что совпадает с числом различных перестановок длины <tex> n</tex>. Это означает, что вероятность выбрать любую перестановку длины <tex> n</tex> равна <tex> \ | + | Корректность данного алгоритма очевидна. На каждой итерации цикла мы выбираем случайный элемент из всех оставшихся, то есть у нас есть <tex> n</tex> способов выбрать <tex>1</tex> элемент, <tex> n - 1</tex> способов выбрать <tex>2</tex> элемент ... <tex> 1</tex> способ выбрать последний элемент. Таким образом, последовательность длины <tex> n</tex> мы можем получить  <tex> $$n \times (n - 1) \times \ldots \times 1 = n! $$ </tex> способами, что совпадает с числом различных перестановок длины <tex> n</tex>. Это означает, что вероятность выбрать любую перестановку длины <tex> n</tex> равна <tex> \dfrac{1}{n!}</tex>, то есть все перестановки равновероятны. | 
| ==Неправильные способы реализации== | ==Неправильные способы реализации== | ||
| Небольшая модификация этого алгоритма, может резко сказаться на его корректности. Например, следующие два алгоритма работают неправильно: | Небольшая модификация этого алгоритма, может резко сказаться на его корректности. Например, следующие два алгоритма работают неправильно: | ||
| − |    '''for''' i = n ''' | + |    '''for''' i = n '''downto''' 1 | 
|      swap(i, random(1..n)) |      swap(i, random(1..n)) | ||
| − |    '''for''' i = n ''' | + |    '''for''' i = n '''downto''' 1 | 
|      swap(random(1..n), random(1..n)) |      swap(random(1..n), random(1..n)) | ||
| В самом деле: число способов сгенерировать последовательность в первом случае равно <tex>n^n</tex>, в то время как существует всего <tex> n!</tex> возможных перестановок из <tex> n</tex> элементов. Поскольку <tex> n^n</tex> никогда не может делиться на <tex> n!</tex> без остатка при <tex> n > 2</tex> (так как <tex> n!</tex> делится на число <tex> n - 1</tex> , которое не имеет с <tex> n</tex> общих простых делителей), то некоторые перестановки должны появляться чаще, чем другие. Аналогично для второго случая, где число способов сгенерировать последовательность равно уже <tex> (n^2)^n</tex>. | В самом деле: число способов сгенерировать последовательность в первом случае равно <tex>n^n</tex>, в то время как существует всего <tex> n!</tex> возможных перестановок из <tex> n</tex> элементов. Поскольку <tex> n^n</tex> никогда не может делиться на <tex> n!</tex> без остатка при <tex> n > 2</tex> (так как <tex> n!</tex> делится на число <tex> n - 1</tex> , которое не имеет с <tex> n</tex> общих простых делителей), то некоторые перестановки должны появляться чаще, чем другие. Аналогично для второго случая, где число способов сгенерировать последовательность равно уже <tex> (n^2)^n</tex>. | ||
| − | + | ||
| − | |||
| − | |||
| ==См.также== | ==См.также== | ||
| *[http://bost.ocks.org/mike/shuffle/ Интерактивный пример] | *[http://bost.ocks.org/mike/shuffle/ Интерактивный пример] | ||
Версия 04:07, 23 января 2016
Тасование Фишера – Йетса (названо в честь Рональда Фишера (Ronald Fisher) и Франка Йетса (Frank Yates)) — алгоритм создания случайных перестановок конечного множества, попросту говоря, для случайного тасования множества. Основная процедура тасования Фишера – Йетса аналогична случайному вытаскиванию записок с числами из шляпы или карт из колоды, один элемент за другим, пока элементы не кончатся. Алгоритм обеспечивает эффективный и строгий метод таких операций, гарантирующий несмещённый результат. Время работы алгоритма
Содержание
Постановка задачи
Необходимо сгенерировать случайную перестановку из чисел с равномерным распределением вероятности, если есть в наличии функция для генерации случайного числа в заданном интервале.
Решение
Пусть 
-  генерирует случайное число в интервале  
Следующий алгоритм решает задачу:
 int[] randomPermutation(int[] a)   //   n — длина перестановки 
   for i = n  downto 1
     j = random(1..i)
     swap(a[i], a[j])
 return a
Обоснование
Корректность данного алгоритма очевидна. На каждой итерации цикла мы выбираем случайный элемент из всех оставшихся, то есть у нас есть способов выбрать элемент, способов выбрать элемент ... способ выбрать последний элемент. Таким образом, последовательность длины мы можем получить способами, что совпадает с числом различных перестановок длины . Это означает, что вероятность выбрать любую перестановку длины равна , то есть все перестановки равновероятны.
Неправильные способы реализации
Небольшая модификация этого алгоритма, может резко сказаться на его корректности. Например, следующие два алгоритма работают неправильно:
for i = n downto 1 swap(i, random(1..n))
for i = n downto 1 swap(random(1..n), random(1..n))
В самом деле: число способов сгенерировать последовательность в первом случае равно , в то время как существует всего возможных перестановок из элементов. Поскольку никогда не может делиться на без остатка при (так как делится на число , которое не имеет с общих простых делителей), то некоторые перестановки должны появляться чаще, чем другие. Аналогично для второго случая, где число способов сгенерировать последовательность равно уже .
См.также
Источники информации
- Д.Э. Кнут Искусство программирования, том 2. Получисленные методы — 3-е изд. — М.: «Вильямс», 2007. — С. 832. — ISBN 0-201-89684-2
- Википедия - Тасование Фишера–Йетса
- Wikipedia - Fisher-Yates shuffle
