所以我遇到了一个概率问题,出于纯粹的无聊,我决定尝试使用模拟来解决。 问题:在6张牌中准确抽出一张高牌的可能性是多少。
现在,这个问题专门针对在我的国家/地区玩的游戏,因此出于某种奇怪的原因,这里有21张高牌,但这并不重要。
现在,这就是我在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;
}
答案 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);
}
但是组合方法有两种错误:
21 31 30 29 28 27 __ . __ . __ . __ . __ . __ = 0.02921 52 51 50 49 48 47
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 ++并使用这些方法,或者找到一种获得良好分布的方法。而且,如果您正在进行仿真以数字方式计算概率,那么这确实很重要。