通过模拟,一张6张牌中的一张高牌确实是

时间:2018-06-30 22:19:24

标签: c probability combinatorics

所以我遇到了一个概率问题,出于纯粹的无聊,我决定尝试使用模拟来解决。 问题:在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);


}

所以 deck 是一个由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 函数以及它的伪随机性有关,但是我真的不知道,因为很难测试任何可以处理随机数的东西。

有什么想法吗?

谢谢。

编辑: 根据klutt的要求:

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

2 个答案:

答案 0 :(得分:1)

我的程序给出的结果与您的结果相同-约为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。
0.02921 * 6 = 0.1752

答案 1 :(得分:0)

您的计算错误

您要计算的是 first 卡高,而其余所有低的概率。或至少表明您正在尝试这样做。你有点不对劲。应该是(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次仿真,每次迭代1千万次:

使用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 ++并使用这些方法,或者找到一种获得良好分布的方法。而且,如果您正在进行仿真以数字方式计算概率,那么这确实很重要。

相关问题