想必大家都jugg都是十分了解的,其大招无敌斩更是他享有“河道杀手”的称号。但是随机性攻击也成为jugg不稳定击杀的隐患。下面我们来考虑一个问题,jugg追杀敌方单位,准备开大,这时突然冒出来一坨小兵(很常见的情形)。这时jugg是否应该继续放大呢?或者放大后有多大的概率击杀对手呢?
显然我们将会通过编程来处理这个问题。在此之前,我想介绍关于面向对象的编程(因为我和我们一些同学正在学习Java以及Android开发,了解面向对象的机制还是很有帮助的)。
面向过程的程序设计我们为了简洁代码量,以函数的方式进行封装,来实现相同的功能。与面向过程的程序设计不同,面向对象的程序设计是以数据而不是功能为核心,将数据以及这些数据的操作以“类”的形式封装为一个整体,以“类”的对象作为程序的基本元素,通过对象发送消息,进而由对象启动相关的方法完成各种功能。形象的说,“类”就是一个包裹,里面放置着数据和函数(方法)。数据与函数分为动态和静态,动态的数据、函数必须实例化才能访问或者运行,而静态的数据、函数至只与类有关,与对象无关。静态函数类似于面向过程,此时类的作用仅仅是归类、打包。
下面来讲讲开始提出的例子,如果我们现在是面向过程,那么我们需要写一个函数,它的参数是jugg剩余攻击次数、近程小兵个数、远程小兵个数,经过函数内部的模拟或者计算,可以通过返回值的形式传出概率结果,也可以通过指针、引用参数的方式为形参赋值。但是,如果我们现在是面向对象,我们需要写一个“概率模拟器”或者是“概率计算器”,实例化“模拟器”或“计算器”对象并设置必要参数、运行得到概率结果。大家可以参考我们现实当中的计算器,通过键入数据、点击运行按钮,从而获得相应的结果,当然前提是你必须先买一个计算器,即实例化计算器对象。
为了解决jugg的无敌斩问题,我们提出几点必要的假设,使之完全成为数学问题:
1.jugg在无敌斩期间忽略普通攻击;
2.jugg无敌斩的劈斩对象概率均相等(这一点假设是必要的,其实jugg是随机一个角度在进行角度劈斩,不能保证劈斩每个人的概率相等);
3.随机数的产生视为真随机数(其实C++模拟随机数、魔兽软件产生的随机数都是伪随机数);
4.jugg的第一次攻击也是随机的(其实是必中的,我们可以通过将剩余次数、劈斩次数同时减一的方法模拟或计算)
4.近程兵能够承受3次攻击,远程兵能够承受2次攻击(实验得来的,但是随着小兵的成长承受次数会增加)。
我们规定在构造函数当中传入必要参数(近程小兵个数、远程小兵个数、剩余攻击次数),并可以人为地设置劈斩次数、模拟次数。最后通过run()函数获得模拟成功概率。
下面我们编写“概率计算器”类,保存在文件calculate.h中:
最后,我们通过main()函数进行测试,由于类的封装性,用户不需详知类的工作原理只要会使用就行了。
这里我们实例化对象并初始化、设置、运行。当然还计算出模拟准确度。
如果jugg可以劈6下,劈到就死(如程序设置),那么运行结果如下:
simulate probability= 76%
calculate probability= 75.974%
accuracy of simulating: 99.9658%
请按任意键继续. . .
如果jugg可以劈9下,那么
simulate probability= 90.9%
calculate probability= 91.4825%
accuracy of simulating: 99.3632%
请按任意键继续. . .
如果只能劈三下。。。。
simulate probability= 45.5%
calculate probability= 49%
accuracy of simulating: 92.8571%
请按任意键继续. . .
可见,模拟概率与计算概率的吻合度还是很高的,证明程序基本正确。
jugg的大招模型可以运用到很多相似的地方:
1.与弹射有关的模型:如lich的大招、WD的弹弹乐
2.准弹射模型:三国杀陆伯言的技能烧营
3.很多概率模型:如多次抽签、判定、拼点等
4.其实jugg的大招概率计算是一个NP问题,经过恰当的算法改造,可以定义公钥密码体制。
等等。
显然我们将会通过编程来处理这个问题。在此之前,我想介绍关于面向对象的编程(因为我和我们一些同学正在学习Java以及Android开发,了解面向对象的机制还是很有帮助的)。
面向过程的程序设计我们为了简洁代码量,以函数的方式进行封装,来实现相同的功能。与面向过程的程序设计不同,面向对象的程序设计是以数据而不是功能为核心,将数据以及这些数据的操作以“类”的形式封装为一个整体,以“类”的对象作为程序的基本元素,通过对象发送消息,进而由对象启动相关的方法完成各种功能。形象的说,“类”就是一个包裹,里面放置着数据和函数(方法)。数据与函数分为动态和静态,动态的数据、函数必须实例化才能访问或者运行,而静态的数据、函数至只与类有关,与对象无关。静态函数类似于面向过程,此时类的作用仅仅是归类、打包。
下面来讲讲开始提出的例子,如果我们现在是面向过程,那么我们需要写一个函数,它的参数是jugg剩余攻击次数、近程小兵个数、远程小兵个数,经过函数内部的模拟或者计算,可以通过返回值的形式传出概率结果,也可以通过指针、引用参数的方式为形参赋值。但是,如果我们现在是面向对象,我们需要写一个“概率模拟器”或者是“概率计算器”,实例化“模拟器”或“计算器”对象并设置必要参数、运行得到概率结果。大家可以参考我们现实当中的计算器,通过键入数据、点击运行按钮,从而获得相应的结果,当然前提是你必须先买一个计算器,即实例化计算器对象。
为了解决jugg的无敌斩问题,我们提出几点必要的假设,使之完全成为数学问题:
1.jugg在无敌斩期间忽略普通攻击;
2.jugg无敌斩的劈斩对象概率均相等(这一点假设是必要的,其实jugg是随机一个角度在进行角度劈斩,不能保证劈斩每个人的概率相等);
3.随机数的产生视为真随机数(其实C++模拟随机数、魔兽软件产生的随机数都是伪随机数);
4.jugg的第一次攻击也是随机的(其实是必中的,我们可以通过将剩余次数、劈斩次数同时减一的方法模拟或计算)
4.近程兵能够承受3次攻击,远程兵能够承受2次攻击(实验得来的,但是随着小兵的成长承受次数会增加)。
下面我们编写“概率模拟器”类,保存在文件simulate.h中:
#include<iostream.h>
#include<stdlib.h>
#include<time.h>
class Simulator
{
private:
int anum;
int bnum;
int leftattack;
int *p1,*p2;
int times;
int cut;
int p;
public:
Simulator(int a,int b,int lefta)
{
anum=a;
bnum=b;
leftattack=lefta;
times=1000;
p1=new int[a];
p2=new int[b];
for(int i=0;i<anum;i++)
p1[i]=0;
for(i=0;i<bnum;i++)
p2[i]=0;
p=leftattack;
cut=6;
}
void setTimes(int t)
{
times=t;
}
void setCut(int c)
{
cut=c;
}
double run()
{
int x,count=0;
int n;
for(int i=0;i<times;i++)
{
n=0;
while(p&&n!=cut)
{
x=rand()%(anum+bnum+1);
if(x<anum)
{
if(p1[x]==3)
continue;
else p1[x]++;
}
else if(x<anum+bnum)
{
if(p2[x-anum]==2)
continue;
else p2[x-anum]++;
}
else
{
p--;
}
n++;
}
if(!p) count++;
for(int i=0;i<anum;i++)
p1[i]=0;
for(i=0;i<bnum;i++)
p2[i]=0;
p=leftattack;
}
cout<<"simulate probability=\t"<<(double)count/times*100<<"%"<<endl;
return (double)count/times;
}
};我们规定在构造函数当中传入必要参数(近程小兵个数、远程小兵个数、剩余攻击次数),并可以人为地设置劈斩次数、模拟次数。最后通过run()函数获得模拟成功概率。
下面我们编写“概率计算器”类,保存在文件calculate.h中:
#include<iostream.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
class Node
{
public:
char *ar;
Node *next;
int pro;
bool success;
Node(int c,char *a)
{
ar=new char[c];
int l=strlen(a);
strcpy(ar,a);
next=NULL;
pro=1;
success=false;
}
};
class Calculator
{
private:
Node* first;
char* temp;
int anum;
int bnum;
int leftattack;
int cut;
int n;
int *array;
double getRes()
{
double res=0;
for(Node* p=first;p;p=p->next)
{
if(p->success)
res+=1.0/p->pro;
}
return res;
}
void calSuccess()
{
for(Node* p=first;p;p=p->next)
{
int i=0;
for(int j=0;j<(int)strlen(p->ar);j++)
if(p->ar[j]-'0'==anum+bnum) i++;
if(i==leftattack)
p->success=true;
}
}
void visitLink()
{
for(Node* p=first;p;p=p->next)
cout<<p->ar<<"\tpro="<<p->pro<<"\t"<<p->success<<endl;
}
int calLive(int *p)
{
int s=0;
for(int i=0;i<anum+bnum+1;i++)
{
if(i<anum)
{
if(p[i]<3) s++;
}
else if(i<anum+bnum)
{
if(p[i]<2) s++;
}
else
{
if(p[i]<cut) s++;
}
}
return s;
}
void calProbability()
{
int *alive=new int[anum+bnum+1];
for(Node* p=first;p;p=p->next)
{
int s=1;
for(int i=0;i<anum+bnum+1;i++)
alive[i]=0;
for(int j=0;j<(int)strlen(p->ar);j++)
{
s*=calLive(alive);
alive[p->ar[j]-'0']++;
}
p->pro=s;
}
}
void Insert(int *a,int l)
{
char *str=new char[cut];
for(int i=0;i<l;i++)
str[i]=a[i]+'0';
str[i]=0;
if(strcmp(temp,str)!=0)
{
if(n==0)
first=new Node(cut,str);
else
{
Node* q=new Node(cut,str);
q->next=first;
first=q;
}
strcpy(temp,str);
}
else strcpy(temp,str);
n++;
}
bool pass(int *a,int step)
{
int count=0;
for(int i=0;i<step;i++)
if(a[i]==a[step])
count++;
count++;
if(a[step]<anum)
{
if(count==4)
return false;
else return true;
}
else if(a[step]<anum+bnum)
{
if(count==3)
return false;
else return true;
}
else
return true;
}
void print(int *a,int l)
{
for(int i=0;i<l;i++)
cout<<a[i]<<" ";
cout<<endl;
}
void print(int *a)
{
for(int i=0;i<cut;i++)
cout<<a[i]<<" ";
cout<<endl;
}
void fillArray(int *a,int step)
{
int count=0;
for(int j=0;j<step-1;j++)
if(a[j]==anum+bnum) count++;
if(!pass(a,step-1))
return;
if(count==leftattack)
{
Insert(a,step-1);
return;
}
if(step==cut)
{
Insert(a,cut);
return;
}
for(int i=0;i<anum+bnum+1;i++)
{
a[step]=i;
fillArray(a,step+1);
}
}
public:
Calculator(int a,int b,int lefta)
{
anum=a;
bnum=b;
leftattack=lefta;
times=1000;
cut=6;
n=0;
temp=new char[cut];
first=NULL;
}
~Calculator()
{
for(Node *p=first->next;p;p=p->next)
{
delete first;
first=p;
}
}
void setCut(int c)
{
cut=c;
}
double run()
{
array=new int[cut];
fillArray(array,0);
calProbability();
calSuccess();
// visitLink();
double x=getRes();
cout<<"calculate probability=\t"<<x*100<<"%"<<endl;
return x;
}
}; 我们规定在构造函数当中传入必要参数(近程小兵个数、远程小兵个数、剩余攻击次数),并可以人为地设置劈斩次数。最后通过run()函数获得计算成功概率。程序通过链表的存储方式存储劈斩次序,变量pro存放的是某一种情况概率的倒数值。最后,我们通过main()函数进行测试,由于类的封装性,用户不需详知类的工作原理只要会使用就行了。
#include"simulate.h"
#include"calculate.h"
#include<math.h>
void main()
{
Simulator sim(3,1,1);
Calculator cal(3,1,1);
sim.setCut(6);
cal.setCut(6);
srand((unsigned)time(NULL));
double ps=sim.run();
double pc=cal.run();
cout<<"accuracy of simulating:\t"<<100-fabs(ps-pc)/pc*100<<"%"<<endl;
system("pause");
}这里我们实例化对象并初始化、设置、运行。当然还计算出模拟准确度。
如果jugg可以劈6下,劈到就死(如程序设置),那么运行结果如下:
simulate probability= 76%
calculate probability= 75.974%
accuracy of simulating: 99.9658%
请按任意键继续. . .
如果jugg可以劈9下,那么
simulate probability= 90.9%
calculate probability= 91.4825%
accuracy of simulating: 99.3632%
请按任意键继续. . .
如果只能劈三下。。。。
simulate probability= 45.5%
calculate probability= 49%
accuracy of simulating: 92.8571%
请按任意键继续. . .
可见,模拟概率与计算概率的吻合度还是很高的,证明程序基本正确。
jugg的大招模型可以运用到很多相似的地方:
1.与弹射有关的模型:如lich的大招、WD的弹弹乐
2.准弹射模型:三国杀陆伯言的技能烧营
3.很多概率模型:如多次抽签、判定、拼点等
4.其实jugg的大招概率计算是一个NP问题,经过恰当的算法改造,可以定义公钥密码体制。
等等。


被折叠的 条评论
为什么被折叠?



