您的位置:首页 > 编程语言 > C语言/C++

抽牌概率

2017-01-05 16:50 411 查看
本节探讨两例:有趣的抽牌概率计算,其中单色的数字牌设计比较简单,而由多花色扑克组成的数字牌涉及编码的转换,其中设计较为复杂,也更具有吸引力;

涉及到概率计算,必须统计事件的总体数与满足指定条件事件的个数,这是概率计算的基础;

抽数字牌

有n张数字牌,数字牌上分别标有整数1,2,3,……,n;

在这n张数字牌中同时抽取3张,记3张牌上的整数之和为素数的概率为p(n);

输入n(n>=10),计算并输出概率p(n)(精确到小数点后3位);

1.说明:

因任3张牌整数之和不小于6,可以排除唯一偶素数“2”;

(1)、试商法判别素数

判别一个大于1的奇数i是否为素数,最简单的是根据素数的定义应用试商法完成,即用奇数(3~sqrt(i))进行试商判别;

为方便素数检测,设置数组q[i],对3n以内的整数i通过试商法给q[i]赋值:若i为素数,q[i]=1;否则q[i]=0;

(2)、枚举循环设计

注意到任取3张牌即所得3个数不能相同。因而设置的i,j,k三重枚举循环的取值范围分别为:i(1~n-2),j(i+1~n-1),k(j+1~n);

这样的枚举循环设置,确保每不同的三张一组抽取,既无遗漏,也没有重复,这一点很重要,如果枚举循环设置不精准,出现遗漏或重复,会直接导致统计结果错误;

设s为从n张中取3张的总数,显然有:

s=(3)C(n)=(n(n-1)(n-2))/6

在内循环中通过w++统计所有不同的抽牌次数,并对取得的3个数应用q[i+j+k]=1判定3个数之和是否为素数,若是素数则通过m++统计和为素数的抽取次数;

(3)、计算概率

循环结束后,比较w与s,若w!=s,说明循环设置出错,退出程序;

只有当w=s时,才计算并输出概率值p=m/w;

2.程序设计:

#include<stdio.h>
#include<math.h>
int main()
{
int i,j,k,n,t,z,q[3000];
long m,s,w;
double p;
printf("请输入牌的张数n(n<1000):");
scanf("%d",&n);
for(i=1;i<=3*n;i++)
q[i]=0;
for(i=3;i<=3*n;i=i+2)
{
t=1;
z=(int)sqrt(i);
for(j=3;j<=z;j=j+2)
if(i%j==0)
{
t=0;
break;
}
if(t==1)          /*奇数i为素数时标记q[i]=1*/
q[i]=1;
}
m=w=0;
for(i=1;i<=n-2;i++)  /*三重循环枚举抽牌*/
for(j=i+1;j<=n-1;j++)
for(k=j+1;k<=n;k++)
{
w++;
if(q[i+j+k]==1)
m++;     /*统计和为素数的数目*/
}
s=n*(n-1)*(n-2)/6;
if(s!=w)
{
printf("统计出现问题!");
return;
}
p=(double)m/w;
printf("在%d张牌取3张不同取法%ld次,和为素数的%ld次\n",n,w,m);
printf("和为素数的概率为%.03f\n",p);
}


3.程序运行示例及其注意事项:

请输入牌的张数n(n<1000):100
在100张牌取3张不同取法161700次,和为素数的30791次
和为素数的概率为0.190


注意:通过三重循环枚举实现3张抽牌是可行的,也是可靠的,由s与w是否相等的检测可知枚举既无重复,也不存在遗漏

抽扑克牌

玩扑克牌是最普及也是最流行的娱乐活动,扑克牌除大小王之外有红心、方片、草花、黑桃4种花色,每一种花色有A,2,……,10,J,Q,K(约定A为数码1,J,Q,K分别为数码11、12、13)共13个数码;

试在扑克牌每一花色的数码靠前的n(2<=n<=13)张牌(即每种花色的数码均为1,2,……,n)组成的4n张牌中抽取3张,3张牌上的数码之和为素数的概率记为p(n);

输入n,计算并输出p(n)(精确到小数点后3位);

1.说明:

在扑克牌的4种花色牌中抽取要比前面单一数字牌抽取复杂,涉及到牌上的原数码与抽取编码之间的转换;

(1)、抽取编号

为实现在4n张牌中每不同的3张都能抽到,把4n张牌进行抽取编号:

任取一种花色其n张牌按顺序抽取编号为1~n(此时抽取编号与牌上数码相同);

第2种花色的n张牌按顺序抽取编号为n+1~2n(即数码为1的编号为n+1,数码为2的编号为n+2,……,类推);

第3种花色的n张牌按顺序抽取编号为2n+1~3n;

最后第4种花色的n张牌按顺序抽取编号为3n+1~4n;

(2)、设置素数检测数组

为方便素数检测,设置数组q[i],对4n以内的整数i通过试商法给q[i]赋值:若i为素数,q[i]=1;否则q[i]=0;

(3)、枚举循环设计

为实现在4n张牌中任取3张牌,设置i,j,k三重枚举循环,其取值范围分别为:i(1~4n-2),j(i+1~4n-1),k(j+1~4n);

这样的枚举循环设置,确保每不同的三张一组抽取,既无遗漏,也没有重复;

(4)、抽取次数的总数

设s为从4n张中抽取3张的总数,显然有:

s = (3)C(4n) = (4n(4n-1)(4n-2))/6

在i,j,k三重枚举循环内通过w++统计所有不同的抽取次数,循环结束后,比较w与s,若w!=s,说明循环设置出错,退出程序;

(5)、恢复牌上的原数码

以上循环中的i,j,k是抽取编号,把这些抽取编号转换为扑克牌的原数码(设置为i1、j1、k1)是必要的,也是设计的关键所在;

若i>n:i1=i%n,此时若i1=0,则i1=n

例如,若n=13时,抽取编号i=39,根据抽取编号的设置可知该牌的原数码应是21%13,该牌的原数码为8;

若n=13时,抽取编号i=39,根据抽取编号的设置可知该牌的原数码应是13,而由求余运算39%13=0,因此把0转换为13是必要的

抽取编号j,k的转换与上面类似;

(6)、判别与计算

应用q[i1+j1+k1]=1判定抽取的3张牌的数字之和为素数,并通过m++统计所要求的“和为素数”的抽取次数;

最后计算并输出概率值p=m/w;

2.程序设计:

#include<stdio.h>
#include<math.h>
int main()
{
int i,j,k,i1,j1,k1,n,t,z,q[3000];
long m,s,w;
double p;
printf("请输入每色牌的张数n(2<=n<=13):");
scanf("%d",&n);
for(i=1;i<=3*n;i++)
q[i]=0;
for(i=3;i<=3*n;i=i+2)
{
t=1;
z=(int)sqrt(i);
for(j=3;j<=z;j=j+2)
if(i%j==0)
{
t=0;
break;
}
if(t==1)
q[i]=1;         /*i为素数时标记q[i]=1*/
}
m=w=0;

4000
for(i=1;i<=4*n-2;i++) /*三重循环枚举抽3张牌*/
for(j=j+1;j<=4*n-1;j++)
for(k=j+1;k<=4*n;k++)
{
w++;
i1=i%n;
j1=j%n;
k1=k%n;
if(i1==0)
i1=n;     /*恢复扑克上的原数码*/
if(j1==0)
j1=n;
if(1==0)
k1=n;
if(q[i1+j1+k1]==1)
m++;      /*统计和为素数的数目*/
}
s=4*n*(4*n-1)*(4*n-2)/6;   /*s为从4n张中取3张的总数*/
if(s!=w)
{
printf("统计出现问题!");
return;
}
p=(double)m/w;
printf("在扑克每色%d张牌中抽取3张有不同取法%ld次,\n",n,w);
printf("其中和为素数的取法%ld次;和为素数的概率为%.3f\n",m,p);
}


3.程序运行示例及其注意事项:

请输入每色牌的张数n(2<=n<=13):13
统计出现问题!在扑克每色13张牌中抽取3张有不同取法22100次,
其中和为素数的取法6068次;和为素数的概率为0.275


注意:以上程序设计的重点也是难点是把抽取编号i、j、k转换为牌的原数码i1、j1、k1,这也是该设计的灵巧之处

拓广抽扑克牌

上面扑克牌各色牌的张数相同,拓广到4色数字牌的张数任意指定;

有4种数字牌,其张数分别为a张,编号为1~a;b张,编号为1~b;c张,编号为1~c;d张,编号为1~d;

试在这4种数字牌共s=a+b+c+d张牌中抽取3张,试求3张牌上的数码之和为素数的概率p;

输入整数a,b,c,d(1~a,b,c,d<=100),计算并输出概率p(精确到小数点后3位);

1.说明:

(1)、抽取编号

为实现在s=a+b+c+d张牌中每不同的3张都能抽到,把s张牌进行抽取编号:

取第1种花色其a张牌按顺序抽取编号为1~a(此时抽取编号与牌上数码相同);

第2种花色的b张牌按顺序抽取编号为a+1~a+b(即数码为1的编号为a+1,数码为2的编号为a+2,……,类推);

第3种花色的c张牌按顺序抽取编号为a+b+1~a+b+c;

最后第4种花色的d张牌按顺序抽取编号为a+b+c+1~a+b+c+d;

(2)、设置素数检测数组

为方便素数检测,设置数组q[i],对s以内的整数i通过试商法给q[i]赋值:

若i为素数,q[i]=1;否则q[i]=0

(3)、枚举循环设计

为实现在s张牌中任取3张牌,设置i,j,k三重枚举循环,其取值范围分别为:i(1~s-2),j(i+1~s-1),k(j+1~s)

这样的枚举循环设置,确保每不同的三张一组抽取,即无遗漏,也没有重复;

(4)、抽取次数的总数

从s张中抽取3张的总数,显然有:

(3)C(s) = (s(s-1)(s-2))/6

在i,j,k三重美剧循环内通过w++统计所有不同的抽取次数,循环结束后,比较w与上式计算的总数是否相等,若不等,说明循环设置出错,退出程序;

(5)、恢复牌上的原数码

以上循环中的i、j、k是抽取编号,把这些抽取编号转换为各色牌的原数码(设置为i1、j1、k1)是必要的,也是设计的关键所在;

设置把抽取编号x(即i、j、k)转换为原数码y(即i1、j1、k1)的转换函数tr():

若x<=a,则y=x;

若a< x且x<=a+b,则y=x-a;

若a+b< x且x<=a+b+c,则y=x-a-b;

若a+b+c< x且x<=a+b+c+d,则y=x-a-b-c;

(6)、判别与计算

应用q[i1+j1+k1]=1判定抽取的3张牌的数字之和为素数,并通过m++统计所要求的“和为素数”的抽取次数;

最后计算并输出概率值p=m/w;

2.程序设计:

#include<stdio.h>
#include<math.h>
int a,b,c,d,x,y;
int main()
{
int i,j,k,i1,j1,k1,s,t,z,q[400];
long m,w;
double p;
int tr();
printf("请输入每色牌的张数a,b,c,d(1<a,b,c,d<=100):");
scanf("%d,%d,%d,%d",&a,&b,&c,&d);
s=a+b+c+d;
for(i=1;i<=s;i++)
q[i]=0;
for(i=3;i<=s;i=i+2)
{
t=1;
z=(int)sqrt(i);
for(j=3;j<=z;j=j+2)
if(i%j==0)
{
t=0;
break;
}
if(t==1)
q[i]=1;         /*i为素数时标记q[i]=1*/
}
m=w=0;
for(i=1;i<=s-2;i++)   /*三重循环枚举抽3张牌*/
for(j=i+1;j<=s-1;j++)
for(k=j+1;k<=s;k++)
{
w++;         /*w为从s张中取3张的总数*/
x=i;
tr();
i1=y;
x=j;
tr();        /*恢复牌上的原数码*/
j1=y;
x=k;
tr();
k1=y;
if(q[i1+j1+k1]==1)
m++;      /*统计和为素数的数目*/
}
if(w!=s*(s-1)*(s-2)/6)
{
printf("统计出现问题!");
return;
}
p=(double)m/w;
printf("在4色共%d张牌中抽取3张有不同取法%ld次,\n",s,w);
printf("其中和为素数的取法%ld次;和为素数的概率为%.3f\n",m,p);
}
int tr()                 /*抽取编号x转换为原数码y的转换函数*/
{
if(x<=a)
y=x;
else if(a<x && x<=a+b)
y=x-a;
else if(a+b<x && x<=a+b+c)
y=x-a-b;
else
y=x-a-b-c;
return y;
}


3.程序运行示例及其注意事项:

请输入每色牌的张数a,b,c,d(1<a,b,c,d<=100):10,20,40,50
在4色共120张牌中抽取3张有不同取法280840次,
其中和为素数的取法64599次;和为素数的概率为0.230


若输入a=b=c=d=13,结果即为“抽扑克牌”的运行结果;

进一步把4色拓广为一般m色,有兴趣的读者请自行探讨;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息