Перетасуйте колоду карт с ограничениями - PullRequest
7 голосов
/ 28 февраля 2011

Вот факты в первую очередь.

В игре бридж есть 4 игроки по имени север, юг, восток и Запад.

Все 52 карты раздаются по 13 карт. каждому игроку.

Есть системы подсчета Хонор. Ace = 4 очка, King = 3 очка, Queen = 2 очков и Джек = 1 балл.

Я создаю «Дилера карт» с ограничениями, где, например, вы можете сказать, что рука, разыгрываемая на север, должна иметь ровно 5 пик и от 13 до 16 очков чести, остальные руки случайные. 1011 *

Как мне сделать это, не влияя на «случайность» наилучшим образом и не имея эффективного кода?

Я пишу код на C # и .Net, но какая-то идея в псевдокоде была бы хороша!

Ответы [ 5 ]

4 голосов
/ 01 марта 2011

Поскольку кто-то уже упомянул мой Deal 3.1, я хотел бы указать на некоторые из оптимизаций, которые я сделал в этом коде.

Прежде всего, чтобы получить наиболее гибкие ограничения, я хотел добавитьполный язык программирования для моего дилера, чтобы вы могли создавать целые библиотеки ограничений с различными типами оценщиков и правил.Я использовал Tcl для этого языка, потому что я уже изучал его для работы, и в 1994 году, когда был выпущен Deal 0.0, Tcl был самым простым языком для встраивания в приложение C.

Во-вторых, мне нужно было ограничениеязык бегать довольно быстро.Ограничения работают глубоко внутри цикла.Довольно много кода у моего дилера - это небольшая оптимизация с помощью справочных таблиц и т. П.

Одна из самых удивительных и простых оптимизаций заключалась в том, чтобы не сдавать карты на место до тех пор, пока на этом месте не будет проверено ограничение.Например, если вы хотите, чтобы север соответствовал ограничению A, а юг соответствовал ограничению B, а ваш код ограничения:

 match constraint A to north
 match constraint B to south

Только тогда, когда вы дойдете до первой строки, вы заполните северную стрелку.Если это не удается, вы отклоняете полную сделку.Если это проходит, затем заполните южную руку и проверьте ее ограничение.Если это не удастся, выбросить всю сделку.В противном случае завершите сделку и примите ее.

Я нашел эту оптимизацию, выполняя какое-то профилирование и заметив, что большую часть времени проводилось в генераторе случайных чисел.

Есть одна причудливая оптимизация,который может работать в некоторых случаях, назовите «умное наложение».

 deal::input smartstack south balanced hcp 20 21

Это генерирует «фабрику» для южной руки, которая занимает некоторое время, но которая затем может очень быстро заполнить одну руку, чтобысоответствовать этому критерию.Из-за проблем, связанных с условной вероятностью, интеллектуальное суммирование может применяться только к одной руке за раз.[*]

Интеллектуальное накопление принимает «класс формы» - в данном случае «сбалансированный», «оценщик удержания», в данном случае «hcp», и диапазон значений для оценщика удержания.«Оценщик удержания» - это любой оценщик, который применяется к каждой масти, а затем суммируется, поэтому hcp, controls, проигравшие, hcp_plus_shape и т. Д. Все являются удерживающими эвалаторами.необходимо принять довольно ограниченный набор значений.Как работает умная укладка?Это может быть немного больше, чем у меня есть время, чтобы публиковать здесь, но это в основном огромный набор таблиц.

Один последний комментарий: Если вы действительно хотите эту программу только для практики торгов, а не для моделирования,Многие из этих оптимизаций, вероятно, не нужны.Это потому, что сама природа практики делает нецелесообразным время для того, чтобы практиковать чрезвычайно редкие предложения.Так что, если у вас есть состояние, которое встречается только один раз в миллиарде сделок, вам действительно не стоит беспокоиться об этом.:)

[Редактировать: Добавить умные детали стеков.]

Хорошо, в костюме точно 8192 = 2 ^ 13 возможных владений.Сгруппируйте их по длине и количеству чести:

 Holdings(length,points) = { set of holdings with this length and honor count }

Итак

 Holdings(3,7) = {AK2, AK3,...,AKT,AQJ}

и пусть

 h(length,points) = |Holdings(length,points)|

Теперь перечислите все фигуры, которые соответствуют вашему состоянию (пики =5):

 5-8-0-0
 5-7-1-0
 5-7-0-1
 ...
 5-0-0-8

Обратите внимание, что коллекция всех возможных форм рук имеет размер 560, поэтому этот список невелик.

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

 Shape    Points per suit
 5-4-4-0  10-3-0-0
 5-4-4-0  10-2-1-0
 5-4-4-0  10-1-2-0
 5-4-4-0  10-0-3-0
 5-4-4-0  9-4-0-0
 ...

Используя наши наборы Holdings (длина, точки), мы можем вычислить количество способов получить каждую из этих строк.Например, для строки 5-4-4-0 10-3-0-0 вы должны иметь:

h(5,10)*h(4,3)*h(4,0)*h(0,0)

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

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

[*] Теоретически вы можете решить эти условные вероятности.проблемы для умного стека с несколькими руками, но решение проблемы сделало бы его эффективным только для крайне редких типов сделок.Это связано с тем, что количество строк в фабричной таблице примерно равно произведению количества строк для укладки в одну руку на количество строк для укладки в другую руку.Кроме того, в таблице h () необходимо указать количество способов деления n карт на раздачи 1, раздача 2 и другие раздачи, что изменяет число значений примерно с 2 ^ 13 до 3 ^ 13 возможных значений,что примерно на два порядка больше.

4 голосов
/ 28 февраля 2011

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

2 голосов
/ 28 февраля 2011

В зависимости от того, насколько быстрым является ваш компьютер, для этого может быть достаточно :

  • Повтор:
    • сделай случайную сделку
  • Пока доска не отвечает всем ограничениям

Как и во всех вопросах производительности, нужно попробовать и посмотреть!

edit Я попробовал и увидел:

done 1000000 hands in 12914 ms, 4424 ok

Это не имеет никакого отношения к оптимизации - и она производит 342 раздачи в секунду, отвечающих вашим критериям «У Северной есть 5 пиков и 13-16 очков чести». Я не знаю деталей вашего заявления, но мне кажется, что этого может быть достаточно.

1 голос
/ 28 февраля 2011

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

Я бы посоветовал вам использовать выборку отклонения: Генерация случайной сделки (без каких-либо ограничений) иПроверьте, удовлетворяет ли оно вашим ограничениям.

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

Для этого сопоставьте каждую руку по 12 байт.целое число (общее количество рук моста вмещается в 12 байтов).Генерация случайного 12-байтового целого числа может быть произведена всего за 3, 4-байтовые вызовы случайных чисел, конечно, поскольку количество раздач не точно , вписывающееся в 12 байтов, вам может потребоваться немного обработки здесь, но я ожидаю, что это не будет слишком много.

У Ричарда Павличека есть отличная страница (с алгоритмами) для сопоставления сделки с числом и обратно.

См. здесь: http://www.rpbridge.net/7z68.htm

Я бы также посоветовал вам взглянуть на существующее программное обеспечение для раздачи бриджей (например, Deal 3.1 , которое свободно доступно).Сделка 3.1 также поддерживает проведение двойного фиктивного анализа.Возможно, вы могли бы заставить его работать на вас без необходимости бросать один из своих.

Надеюсь, что это поможет.

1 голос
/ 28 февраля 2011

Я бы пошел на этот поток, который, я думаю, не влияет на случайность (кроме как путем сокращения решений, которые не соответствуют ограничениям):

  • Перечислите в своей программе все возможные комбинации "ценных" карт, общее количество очков чести которых находится в диапазоне от 13 до 16. Затем случайным образом выберите одну из этих комбинаций, удалив карты из новой колоды.
  • Подсчитайте, сколько пик у вас уже есть среди ценных карт, и выбирайте случайным образом среди оставшихся пик колоды, пока не встретите счет.
  • Теперь возьмите из колоды столько карт, сколько нужно, чтобы собрать руку.
  • Наконец, возьмите другие руки среди оставшихся карт.

Вы можете написать программу, которая генерирует комбинации из моей первой точки, или просто жестко закодировать их, учитывая симметрию цвета, чтобы уменьшить количество строк кода:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...