Как перебрать все комбинации, например. 48 выбрать 5

Возможный дубликат:
Как итеративно генерировать подмножества k элементов из набора размера n в java?

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

Если два игрока получат по две карты, то в колоде останется 48 карт. В техасском холдеме затем сдаются еще 5 возможных общих карт (это называется доской). Я хочу перечислить/перебрать все 48, выбрать 5 возможных комбинаций досок и подсчитать, сколько раз выигрывает игрок А, сколько раз выигрывает игрок Б и когда они связывают.

Я не уверен, как я могу систематически перебирать каждую комбинацию из 5 карт. У кого-нибудь есть идеи? Карты представлены в виде массива класса Card, но я мог бы также представить их в виде набора битов, если это ускорит работу.

Я делаю это на Java.

Большое спасибо


person Joe    schedule 04.12.2011    source источник
comment
Есть ли у кого-нибудь идеи? 1) Найдите дубликаты, прежде чем задавать вопрос 2) Задайте (конкретный) вопрос. 3) Будь добр к своей маме.   -  person Andrew Thompson    schedule 04.12.2011
comment
@Эндрю Томпсон: за исключением того, что его вопрос очень специфичен. В частности, речь идет об оценке всех возможных матчей C(48,5) между двумя игроками, которые будут олл-ин (по крайней мере, один олл-ин) на префлопе в игре в техасский холдем. Насколько конкретнее это может быть? Он даже продолжает объяснять, как он смоделировал карты и что у него есть собственный оценщик рук (очевидно). Итак, его вопрос касается создания досок C(48,5). Смотрите мой ответ.   -  person TacticalCoder    schedule 05.12.2011
comment
@user988052 user988052 «Заявление о необходимости» (конкретное или нет) не приравнивается к вопросу. Единственный вопрос, заданный в этом посте, - это предложение, которое я процитировал.   -  person Andrew Thompson    schedule 05.12.2011
comment
@Эндрю Томпсон: ну, за исключением того, что контекст очень очевиден из самой фразы прямо перед вопросом в том же абзаце: Я не уверен, как я могу систематически перебирать каждые 5 комбинаций карт. У кого-нибудь есть идеи?. Я помог, ответив на (очевидный) вопрос ОП. Как насчет того, чтобы также помочь, используя вашего представителя, чтобы переформулировать вопрос?   -  person TacticalCoder    schedule 05.12.2011


Ответы (2)


(отказ от ответственности: я написал очень быстрый анализатор покерных рук)

Я хочу перечислить/перебрать все 48, выбрать 5 возможных комбинаций досок и подсчитать, сколько раз выигрывает игрок А, сколько раз выигрывает игрок Б и когда они связывают.

Вы не хотите оценивать руки C(48,5) (1 712 304) каждый раз, когда у вас есть матч между двумя игроками на префлопе: большинство программ просто используют предварительно вычисленную справочную таблицу между всеми возможными матчами между двумя игроками на префлопе.

Например, скажем, у вас есть «Ac Ad» против «7c 6c», вы просто ищете в таблице поиска, которая содержит: 1 333 573, 371 831, 6900 (где 1 333 573 — это количество раз, когда «Ac Ad» выигрывает, 371 831 — это количество раз, когда Выигрывает "7c 6c" и 6 900 - это количество ничьих (в сумме они составляют 1 712 304). Чтобы получить место, вы можете отбросить 6 900, зная, что количество ничьих всегда должно быть C(48,5 ) - (выигрыш 1 + выигрыш 2).

(подробнее о таблице поиска в конце этого ответа)

Но чтобы ответить на ваш вопрос:

Я не уверен, как я могу систематически перебирать каждую комбинацию из 5 карт.

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

Когда вам нужна такая высокопроизводительная «обработка чисел», вы можете забыть о «шаблонах проектирования» и «ОО». То, что вы хотите, это чистая скорость.

Например, следующий код будет проходить через самый внутренний цикл C(48,5) раз, и это довольно быстро:

    for ( int i = 0; i < n; i++ ) {
        for ( int j = i + 1; j < n; j++ ) {
            for ( int k = j + 1; k < n; k++ ) {
                for (int l = k + 1; l < n; l++) {
                    for (int m = l + 1; m < n; m++) {
                        ...
                    }
                }
            }
        }
    }

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

Но для трех игроков на префлопе (где нецелесообразно использовать столы на префлопе, слишком много матч-апов) вы можете зациклиться таким образом, по C(46,5) рукам, используя пять вложенных циклов (конечно, вам нужно используйте i,j,k,l,m, чтобы получить правильные 5 карт из 46 оставшихся карт). Затем, когда у вас есть 5 карт, вы используете быстрый оценщик рук, который дает вам лучшее из 7 (5 карт на столе + по две карты каждого игрока).

Относительно таблицы поиска:

Большинство людей используют приблизительную справочную таблицу 169 против 169 («Ac Kd», «As Kh», «Ad Ks» и т. д., все они становятся «AK разномастными», а возможные стартовые руки C (52,2) группируются в тип 169. стартовых рук). Статья в Википедии объясняет, как получить 169 неэквивалентных стартовых рук:

http://en.wikipedia.org/wiki/Texas_hold_%27em_starting_hands

Они не эквивалентны, если принять во внимание одну руку, но как только вы сравниваете руку 1 и руку 2, "169 против 169" является приблизительным (довольно хорошо что сказал).

Конечно, вы можете стать более любознательным. Есть только C(52,2) (что дает 1326) реальных разных стартовых рук в Холдеме, а это означает, что очень практично построить идеальную справочную таблицу (вообще без приближений) на современных компьютерах (C(1326,2) в не такой большой), если вам действительно нужны идеальные числа. Если вы можете жить с приближением, перейдите к таблице 169 против 169 (для этого потребуется C (169,2) или 14 196 записей).

person TacticalCoder    schedule 04.12.2011
comment
Спасибо за ваш фантастический ответ! Так полезно. На самом деле причина, по которой я вообще пытаюсь построить это, заключается в том, что я получил справочную таблицу 169 против 169 с pokerstove.com/analysis/preflopeq.php и пытался выяснить, как были получены цифры и как я мог бы объединить значения отдельных рук и эквити рук, чтобы получить значения диапазонов против диапазонов. Казалось, что если бы я мог построить свою собственную реализацию, то этот опыт обучения прояснил бы математику. Еще раз спасибо за ваш ответ. - person Joe; 05.12.2011
comment
@Joe: нет проблем, для этого и предназначен StackOverflow :) Что касается индивидуальной руки и диапазона или диапазона и диапазона, для двух игроков: вы можете просто выполнить несколько поисковых запросов. в таблице 169 против 169. Например, вам нужно JJ против диапазона AA, KK и QQ, вы просто играете JJ против AA + JJ против KK + JJ против QQ / 3. Это немного сложнее, но в основном это один из способов. И вы всегда можете использовать PokerStove для проверки своих результатов :) - person TacticalCoder; 05.12.2011
comment
@TacticalCoder, однако приведенный выше код является статическим. Когда число for является динамическим, этот подход не будет работать. - person Pacerier; 18.01.2016

  1. Инициализируйте массив (A) первыми 5 индексами. (0,1,2,3,4)
  2. Переместите последний возможный индекс в A на следующую позицию. A[k]++
  3. Переместите следующие индексы на следующие последовательные позиции. (A[k+1] = A[k] + 1, ...). Если какой-то индекс станет слишком большим, попробуйте использовать более ранний индекс на шаге 2.
  4. Выдать элементы с текущими индексами в A.
  5. Если возможно, повторите с шага 2.

Реализовано как итератор:

public class CombinationIterator<T>
        implements Iterable<List<T>>,
                   Iterator<List<T>> {
    private List<T> items;
    private int choose;
    private boolean started;
    private boolean finished;
    private int[] current;

    public CombinationIterator(List<T> items, int choose) {
        if (items == null || items.size() == 0) {
            throw new IllegalArgumentException("items");
        }
        if (choose <= 0 || choose > items.size()) {
            throw new IllegalArgumentException("choose");
        }
        this.items = items;
        this.choose = choose;
        this.finished = false;
    }

    public Iterator<List<T>> iterator() {
        return this;
    }

    public boolean hasNext() {
        return !finished;
    }

    public List<T> next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }

        if (current == null) {
            current = new int[choose];
            for (int i = 0; i < choose; i++) {
                current[i] = i;
            }
        }

        List<T> result = new ArrayList<T>(choose);
        for (int i = 0; i < choose; i++) {
            result.add(items.get(current[i]));
        }

        int n = items.size();
        finished = true;
        for (int i = choose - 1; i >= 0; i--) {
            if (current[i] < n - choose + i) {
                current[i]++;
                for (int j = i + 1; j < choose; j++) {
                    current[j] = current[i] - i + j;
                }
                finished = false;
                break;
            }
        }

        return result;
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }
}

Эквивалентно в C# с использованием метода Iterator:

public IEnumerable<IList<T>> Combinations<T>(IEnumerable<T> items, int choose)
{
    if (items == null) throw new ArgumentNullException("items");

    var itemsList = items.ToList();
    int n = itemsList.Count;

    if (n < 1) throw new ArgumentException("Must contain at least one item.", "items");
    if (choose <= 0 || choose >= n) throw new ArgumentOutOfRangeException("choose");

    var indices = Enumerable.Range(0, choose).ToArray();

    bool moreWork = true;
    while (moreWork)
    {
        yield return indices.Select(i => itemsList[i]).ToList();

        moreWork = false;
        for (int i = choose - 1; i >= 0; i--)
        {
            if (indices[i] < n - choose + i)
            {
                indices[i]++;
                for (int j = i + 1; j < choose; j++)
                {
                    indices[j] = indices[i] - i + j;
                }
                moreWork = true;
                break;
            }
        }
    }
}
person Markus Jarderot    schedule 04.12.2011
comment
Спасибо за полезный фрагмент кода. - person Erel Segal-Halevi; 25.01.2012
comment
Удивительно полезно. Я перенес код на C# для использования с foreach. Однако метод немного отличается: Boolean MoveNext() для подготовки следующего результата и возврата, есть ли он, и свойство List<T> Current для получения этого результата. Это просто означало, что подготовленный результат должен был быть сохранен, но, кроме того, очень мало изменений. - person Nyerguds; 20.03.2013
comment
@Nyerguds Я добавил версию C #, лучше используя языковые соглашения. - person Markus Jarderot; 20.03.2013
comment
О, хорошенькая. Я использовал этот код для оптимизации папок проекта с 4 приложениями, включающими много одинаковых DLL. Установленный конечный результат попадет в одну папку, но чтобы иметь возможность иметь установщик с выбором компонентов, они были созданы для отдельных папок. Этот код очень помог мне его оптимизировать и переместить все общие файлы в разные папки :) - person Nyerguds; 21.03.2013