Как правильно перетасовать колоду карт

Когда мне нужно перетасовать колоду покерных карт в Java/Android, я, конечно же, использую Collections.shuffle(List<?> list). Я когда-либо делал это, и результаты казались приемлемыми. Но это не так.

Как указано в этой статье, их 52! возможные уникальные перетасовки покерной колоды из 52 карт. Это составляет около 2^226.

Но Collections.shuffle(List<?> list) использует new Random() по умолчанию, который использует 48-битный seed и поэтому может создать только 2^48 уникальных перетасовок, что составляет всего 3.49*10^(-52) процента всех возможных перетасовок!

Так как же правильно тасовать карты?

Я начал использовать SecureRandom, но достаточно ли этого?

List<Card> cards = new ArrayList<Card>();
...
SecureRandom secureRandom;
try {
    secureRandom = SecureRandom.getInstance("SHA1PRNG");
}
catch (NoSuchAlgorithmException e) {
    secureRandom = new SecureRandom();
}
secureRandom.nextBytes(new byte[20]); // force SecureRandom to seed itself
Collections.shuffle(cards, secureRandom);

person caw    schedule 03.05.2013    source источник
comment
Из статьи: Первое, что нужно понять, это то, что алгоритм, способный производить каждое из 52! перемешивание на самом деле не требуется.   -  person flup    schedule 03.05.2013
comment
вы можете сгенерировать случайное число, которое определяет, как часто вы тасуете колоду   -  person Philipp Sander    schedule 03.05.2013
comment
@Philipp Sander: Повторяющаяся перетасовка не увеличивает случайность, не так ли?   -  person caw    schedule 03.05.2013
comment
чем не использовать Collections#shuffle? каждое перемешивание генерирует новую (и случайную) начальную ситуацию для перемешивания   -  person Philipp Sander    schedule 03.05.2013
comment
Потому что он может генерировать только невероятно небольшое количество всех возможных перетасовок, как я написал в вопросе.   -  person caw    schedule 03.05.2013
comment
блог .uncommons.org/2008/04/10/ Это может помочь, я также обновил свой ответ на не совсем бессмысленный в соответствии со статьей.   -  person zw324    schedule 03.05.2013
comment
Почему бы не заменить генератор на более качественный, чем стандартный? (en.wikipedia.org/wiki/Mersenne_twister).   -  person pjs    schedule 03.05.2013
comment
@pjs: Разве Mersenne Twister не 32-битный или 64-битный, а SHA1PRNG 160-битный?   -  person caw    schedule 03.05.2013
comment
@MarcoW.Mersenne Twister выдает 32-битные или 64-битные результаты, но он основан на гораздо большем внутреннем состоянии - его длина цикла составляет 2 ^ 19937-1.   -  person pjs    schedule 03.05.2013
comment
Имеет ли это какое-то значение, если вывод только 64-битный? Так как это то, с чем вы сеете PRNG, не так ли?   -  person caw    schedule 04.05.2013
comment
@МаркоВ. Я верю, что да. В любом генераторе с одиночным начальным значением, таком как LCG, как только вы увидите заданное значение во второй раз, вся последовательность будет повторяться идентично. Когда большее состояние сворачивается/проецируется на меньшее выходное состояние, повторение одного и того же значения не означает, что вы получите повторение той же последовательности. Что касается посева, это просто выбор точки входа в большой цикл. 64-битное начальное число означает, что вы выбираете одну из примерно 10 ^ 18 точек входа в цикл длиной 2 ^ 19937-1.   -  person pjs    schedule 04.05.2013


Ответы (5)


Вы можете получить только 248 разных рук из определенной начальной расстановки, но нет необходимости каждый раз начинать с одной и той же расстановки.

Предположительно, после того, как колода будет закончена (руки в покере, блэкджек и т. д.), она будет в неопределенном порядке, и подойдет любая из этих перестановок.

И, если вас беспокоит тот факт, что вы начинаете с фиксированного расположения каждый раз, когда запускаете свою программу, просто сохраните порядок при выходе и перезагрузите его в следующий раз.

В любом случае, 248 — это по-прежнему огромное количество возможностей (около 280 000 000 000 000), более чем достаточное для карточной игры, тем более, когда вы понимаете, что это ограничивает перетасовку, а не аранжировку. Если вы не серьезный статистик или криптограф, то, что у вас есть, должно подойти.

person paxdiablo    schedule 03.05.2013
comment
Presumably, after the deck is finished (poker hands, blackjack and so on), it will be in an indeterminate order Вероятно, Блэкджек здесь не очень подходит, так как стратегия слишком фиксированная. Также я сомневаюсь в других играх, так как правила в значительной степени исключали большинство перестановок. - person zw324; 03.05.2013
comment
Это не будет в неопределенном порядке. Это будет порядок, которым вы действительно можете манипулировать в предыдущей игре. - person flup; 03.05.2013
comment
Как 2^48 может быть достаточно, даже для карточной игры, если это только 0.000000000000000000000000000000000000000000000000000349% из всех возможных перетасовок? Некоторые никогда не будут достигнуты. И из-за проблем с реализацией вам обычно приходится каждый раз начинать с одного и того же фиксированного расположения. - person caw; 03.05.2013
comment
Если все будут следовать одним и тем же правилам, то, возможно, следующая отправная точка будет определена, но это маловероятно. - person paxdiablo; 03.05.2013
comment
@Марко, посмотри мои первые несколько заявлений. Вы не начинаете с одной и той же аранжировки перед каждым перемешиванием, поэтому вы не ограничены 2^48 аранжировкой. Конечно, есть только 2^48 возможных переходов, но это не аранжировки. Кроме того, скорее всего вы сыграете 280 триллионов раздач? :-) - person paxdiablo; 03.05.2013
comment
Имеет ли это значение, сыграю ли я 10 рук или 280 триллионов? Я думаю нет. Потому что, если есть какие-то договоренности, которые никогда не будут достигнуты (их огромное количество), может быть, некоторые с флеш-роялем, я никогда их не увижу. Это меняет игру. Неважно, сыграю ли я 10 рук или 280 трлн. И всякий раз, когда моя программа перезапускается, я снова начинаю с того же порядка, который зафиксирован в моем коде. - person caw; 03.05.2013
comment
@aMarco, я бы посоветовал это исправить. Другие варианты могут состоять в том, чтобы ввести истинную случайность, такую ​​как использование времени из пользовательского ввода для преобразования последовательности случайных чисел (например, вызов rand каждые 0,1 секунды во время простоя). Но я действительно думаю, что это из-за инженерии. Будет достаточно простого исправления, заключающегося в том, чтобы не начинать с той же аранжировки. - person paxdiablo; 03.05.2013
comment
Хотя 48-разрядность — это огромное число в абсолютном выражении, что это дает вам, если это невероятно маленькое число в относительном выражении? Таким образом, не должно быть субъективного вопроса, считает ли кто-то этого достаточным или нет. Должно быть очевидно, что это слишком мало. - person caw; 03.05.2013
comment
Но вы бы сказали, что Random или SecureRandom в Java — это лучший ГСЧ, который можно использовать без потери эффективности, верно? И просто перетасовывая старые (уже перетасованные) карты снова и снова, проблема последовательностей тасования, которые кажутся нереальными, должна быть решена, не так ли? - person caw; 03.05.2013
comment
@ Марко, я не уверен, что доношу свою точку зрения. Небольшое количество результатов для случайной последовательности не имеет значения, поскольку вы не используете ее из согласованной начальной схемы. Рассмотрим (очень плохую) функцию перетасовки, которая меняет местами только карты 1 и 2. Если вы перетасовываете, а затем играете в игру, в которой карты остаются в другом начальном расположении, это перетасовка не даст того же расположения в следующий раз, несмотря на то, что оно позволяет только 2 ^0 переходов. Это промежуточная игра, которая обеспечивает дополнительную случайность, и все 52! возможны договоренности. - person paxdiablo; 04.05.2013
comment
Спасибо, я думаю, что понял вашу мысль ;) Но это работает не только в том случае, если в предыдущей игре карты были оставлены в новом порядке, но и в том случае, если вы просто снова перетасуете перетасованную колоду для следующей игры, верно? Поскольку в этом случае расположение тоже отличается, и хотя вы можете достичь только 2^48 или 2^160 переходов, в конечном итоге вы сможете достичь всех 52! распоряжения. - person caw; 04.05.2013

Хотя вы используете SecureRandom, он по-прежнему имеет ограниченное состояние. Пока это входное начальное число имеет меньший диапазон, чем 52! оно не может быть совершенно случайным.

На самом деле, SHA1PRNG имеет 160-битное заполнение, что означает, что он все еще недостаточно случайный. Подпишитесь на эту link, несколько лет назад у него было решение с использованием сторонней библиотеки под названием UnCommons Math.

person zw324    schedule 03.05.2013
comment
Хорошо, спасибо! 160 бит определенно намного лучше, чем 48 бит, так что это хороший первый шаг. Где источник для SHA1PRNG, имеющий 160 бит, кроме страницы, на которую вы ссылаетесь? - person caw; 03.05.2013
comment
В Вики есть это: en.wikipedia.org/wiki/SHA-1. Согласно Википедии, SHA-2 или 3 также могут работать. - person zw324; 03.05.2013
comment
Таким образом, SHA2PRNG будет достаточно, если SHA-256 имеет достаточно битов для генерации всех возможных перетасовок. - person caw; 03.05.2013
comment
Хотя это приятное ощущение, если начальное пространство равно количеству возможных перетасовок, начальное число не обязательно должно быть таким же большим, как проблемное пространство. Он должен быть достаточно большим, чтобы его нельзя было взломать, а случайные числа должны быть распределены достаточно равномерно, чтобы руки не были предсказуемы. Неважно, что вы упускаете возможные флеши, если вы не пропускаете больше флешей, чем нефлеев. - person flup; 03.05.2013
comment
Возможно, вам также следует включить заголовок в ссылку, чтобы ссылку можно было лучше найти в Google (если URL-адрес разрешается в 404). Другими словами, я предлагаю заменить Перейти по этой ссылке чем-то вроде Читать Новые приключения в программном обеспечении »Руководство Java-программиста по случайным числам, часть 3: раздача - person Wolf; 23.11.2014

Если вам нужна настоящая случайность, вы можете просто пропустить псевдослучайные генераторы и выбрать что-то получше, например, случайные числа, сгенерированные из атмосферного шума.

random.org предлагает API для интеграции сгенерированных таким образом случайных чисел в ваше собственное программное обеспечение.

person Nicktar    schedule 03.05.2013

Кража ответа из статьи, на которую вы ссылаетесь:

START WITH FRESH DECK
GET RANDOM SEED
FOR CT = 1, WHILE CT <= 52, DO
X = RANDOM NUMBER BETWEEN CT AND 52 INCLUSIVE
SWAP DECK[CT] WITH DECK[X]

Генератор случайных чисел должен быть хорошим и использовать 64-битное начальное число, которое вы выбираете непредсказуемо, желательно с использованием аппаратного обеспечения.

person flup    schedule 03.05.2013
comment
Алгоритм перетасовки не проблема. Java Collections.shuffle(...) должен использовать перетасовку Фишера-Йейтса, которая является адекватной. Но PRNG и его начальное число являются решающим моментом. - person caw; 03.05.2013
comment
Согласен, ключ в генерации случайных чисел. (Хотя в статье описывается, как людям удавалось неправильно перетасовывать.) - person flup; 03.05.2013

Как действительно перетасовать колоду?

Существует несколько техник перетасовки.

Либо (зачистка/накладная):

Cut the deck in two
    Add a small (pseudorandom) amount of one half to the front of the front of the other
    Add a small (pseudorandom) amount of one half to the front of the back of the other
Do this until one hand is empty
Repeat

Или (Риффл):

Cut the deck in two
    Set down a small (pseudorandom) portion of one half
    Set down a small (pseudorandom) portion of the other
Do this until both hands are empty, and you have a new deck
Repeat

И помимо этого есть еще кое-что, как подробно описано в моей ссылке выше.


Несмотря на это, существует так много комбинаций, что даже идеальный алгоритм перетасовки потребовал бы, чтобы машина исследовала 2*10^50 уникальных перестановок в секунду, чтобы завершить исследование каждой перестановки за время существования Вселенной. Прогнозируется, что к 2019 году современные компьютеры достигнут 1 ExaFLOP (1*10^18 операций с плавающей запятой в секунду).

Ни один человек, тасующий карты, также не будет исследовать этот диапазон возможностей, и вы, я полагаю (на самом базовом уровне), имитируете человеческую перетасовку, верно? Считаете ли вы вероятным, что крупье сможет перетасовать колоду в возрастающем порядке в порядке убывания за одну перетасовку? Разделить колоду с четными разрядами перед нечетными в одну тасовку?

Я не считаю неприемлемым ограничивать себя (хотя и чрезвычайно) небольшой частью этого фазового пространства (2^48 возможных случайных чисел) в каждом тасовании, если вы не постоянно засеиваете одним и тем же образом и т. д.

Существует ровно 52 факториала (сокращенно 52!) возможных порядков карт в колоде из 52 карт. Это примерно 8×1067 возможных порядков или, в частности: 80,658,175,170,943,878,571,660,636,856,403,766,975,289,505,440,883,277,824,000,000,000,000.
Величина этого числа означает, что крайне маловероятно, что две случайно выбранные, действительно рандомизированные колоды когда-либо будут даже в истории Вселенной быть одинаковым. Однако, хотя точная последовательность всех карт в рандомизированной колоде непредсказуема, можно сделать некоторые вероятностные прогнозы относительно колоды, которая недостаточно рандомизирована.
~Википедия

Кроме того, стоит отметить, что Bayer & Diaconis в 1992 году доказали, что для правильной рандомизации колоды требуется всего 7 хороших перетасовок, вот раздел об этом из Википедии, в котором есть много ссылок на статьи, обсуждающие это.

person Pureferret    schedule 29.06.2013