使用模拟在一手 6 张牌中恰好有一张大牌
Exactly one high card in a 6 card hand using simulations
所以我有一个概率问题(出于纯粹的无聊)我决定尝试使用模拟来解决。
问题:在一手 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);
}
因此,牌组 是一个包含 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.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 "low" 张牌,所以表达式应该是
21 31 30 29 28 27
__ . __ . __ . __ . __ . __ = 0.02921
52 51 50 49 48 47
- 其次,6次开奖中的任何一次都可以是"high",而不仅仅是第一次,所以
将机会乘以 6。
0.02921 * 6 = 0.1752
你的计算有误
你计算的是第一张牌是高牌而其余牌是低牌的概率。或者至少看起来你正在尝试这样做。你有点偏离。应该是(21/52)*(31/51)*(30/50)*(29/49)*(28/48)*(27/47) = 0.02921...
你应该把它乘以 6,因为高牌可以出现在任何地方。那么你有恰好一个高的概率,即 0.17526
rand() % n
分布不均匀
话虽这么说,但请注意 C 中的 运行dom 生成器不太适合以这种方式使用。根据您使用它的方式,它可能会出现可怕的分布。如果您使用的是 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 次,每次迭代 1000 万次:
使用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++ 并使用这些方法,要么找到一种获得良好分布的方法。如果您正在进行模拟以数值计算概率,那么它 很重要。
所以我有一个概率问题(出于纯粹的无聊)我决定尝试使用模拟来解决。 问题:在一手 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);
}
因此,牌组 是一个包含 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.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 "low" 张牌,所以表达式应该是
21 31 30 29 28 27 __ . __ . __ . __ . __ . __ = 0.02921 52 51 50 49 48 47
- 其次,6次开奖中的任何一次都可以是"high",而不仅仅是第一次,所以 将机会乘以 6。
0.02921 * 6 = 0.1752
你的计算有误
你计算的是第一张牌是高牌而其余牌是低牌的概率。或者至少看起来你正在尝试这样做。你有点偏离。应该是(21/52)*(31/51)*(30/50)*(29/49)*(28/48)*(27/47) = 0.02921...
你应该把它乘以 6,因为高牌可以出现在任何地方。那么你有恰好一个高的概率,即 0.17526
rand() % n
分布不均匀
话虽这么说,但请注意 C 中的 运行dom 生成器不太适合以这种方式使用。根据您使用它的方式,它可能会出现可怕的分布。如果您使用的是 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 次,每次迭代 1000 万次:
使用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++ 并使用这些方法,要么找到一种获得良好分布的方法。如果您正在进行模拟以数值计算概率,那么它 很重要。