Ровно одна старшая карта в 6-карточной руке с использованием симуляции - PullRequest
0 голосов
/ 01 июля 2018

Итак, у меня есть проблема вероятности, которую (из чистой скуки) я решил попробовать и решить с помощью симуляций. Проблема: Какова вероятность вытянуть ровно одну старшую карту из 6-карточной руки.

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

Решая эту проблему вручную, используя базовую комбинаторику, я получил: enter image description here

А теперь, вот как я смоделировал это в C:

Основная функция:

int main(void)
{

    srand(time(0));
    int deck[52];

    int i;
    for(i = 0; i<21; i++) deck[i] = 1;
    for(i = 21; i<52; i++) deck[i] = 0;

    int n;
    printf("# of simulations: ");
    scanf("%d", &n);

    int memo[52];

    int hits = 0;
    for(i = 0; i<n; i++){
      clear_memo(memo);
      hits += simulate(deck, memo);
    }
    printf("Result: %lf\n", (double)hits/n);


}

Таким образом, колода представляет собой массив из 52 чисел, где первые 21 имеют значение 1 (старшие карты), а остальные 31 элемент имеют значение 0 (младшие карточки).

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

Затем он вызывает функции симуляции и подсчитывает попадания.

Вот функция симуляции:

int simulate(int * deck, int * memo){

  //I draw the first card separetly in order to initialize the had_high variable
  int index = ( rand() % 52 );
  int card = deck[index];
  int had_high = (card == 1);
  memo[index] = 1;

  //printf("%d ", index);

  int i = 1;
  while(i < 6){

    int draw = (rand() % 52);
    //printf("%d ", draw);
    if(memo[draw]) continue;

    index = draw;
    card = deck[index];
    memo[index] = 1;

    if(card){
        if(had_high) { //meaning there are 2 high cards, no hit
          //printf("\n");
          return 0;
        }
        had_high = 1; //if not, then this is the first high card
    }

    i++;
  }
  printf("\n");
  return had_high; //the function would've reached this point even if all the cards had been low
                   //therefore I return had_high instead of just 1

}

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

Однако, когда я запускаю программу с большим количеством симуляций (100k или 1m), результат всегда составляет ок. 0,175 - это не то, что я получил при расчете вручную.

Я достаточно уверен, что мои расчеты вручную верны (но поправьте меня, если я тоже ошибаюсь).

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

Есть идеи?

Спасибо.

EDIT: Согласно запросу klutt:

void clear_memo(int * memo){
  int i = 0;
  for(;i<52;i++) memo[i] = 0;
}

Ответы [ 2 ]

0 голосов
/ 01 июля 2018

Ваш расчет неверен

Вы рассчитываете вероятность того, что первая карта является высокой, а все остальные низкими. Или, по крайней мере, кажется, что вы пытаетесь это сделать. Вы немного не в себе. Это должно быть (21/52)*(31/51)*(30/50)*(29/49)*(28/48)*(27/47) = 0.02921...

Вы должны умножить это на 6, так как старшая карта может появиться где угодно. Тогда у вас есть вероятность ровно одного максимума, который равен 0,17526

.

rand() % n имеет неравномерное распределение

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

std::random_device rd;
std::default_random_engine generator(rd());
std::uniform_int_distribution<int> distribution(0,51);
int index = distribution(generator);

В подобных симуляциях это может иметь огромный эффект. В этом случае это имело небольшой эффект. Я протестировал как стандартный метод rand(), так и вариант C ++ и провел симуляцию 4 раза с 10 миллионами итераций каждый раз:

Использование rand() % 52:

Result: 0.175141
Result: 0.175074
Result: 0.175318
Result: 0.175506

Использование distribution(generator):

Result: 0.175197
Result: 0.175225
Result: 0.175228
Result: 0.175293

Как видите, отклонение меньше. Поэтому, если точность имеет значение, либо подумайте о переходе на C ++ и используйте эти методы, либо найдите способ получить хорошее распространение. А если вы выполняете симуляцию для численного расчета вероятности, то это имеет значение .

0 голосов
/ 01 июля 2018

Моя программа дает тот же результат, что и ваша - около 0,175

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
    int deck[52];
    int successes = 0;

    srand((unsigned)time(NULL));
    for(int run = 0; run < 100000; run++) {
        for(int n = 0; n<52; n++) {
            deck[n] = n;
        }
        int cards = 52;
        int highs = 0;
        for(int n=0; n<6; n++) {
            int index = rand() % cards;
            if(deck[index ] < 21) {
                highs++;
            }
            deck[index] = deck[--cards];
        }
        if(highs == 1) {
            successes++;
        }
    }
    printf("Probability of drawing exactly one high card = %f\n", successes / 100000.0);
}

Но комбинатрика ошибочна в двух отношениях:

  • В колоде только 31 "младшая" карта, поэтому выражение должно быть
21   31   30   29   28   27
__ . __ . __ . __ . __ . __   = 0.02921

52   51   50   49   48   47
  • Во-вторых, любой из 6 розыгрышей может быть «высоким», а не только первым, поэтому умножьте шанс на 6.
0.02921 * 6 = 0.1752
...