old_blog 剑圣无敌斩的编程模型

  想必大家都jugg都是十分了解的,其大招无敌斩更是他享有“河道杀手”的称号。但是随机性攻击也成为jugg不稳定击杀的隐患。下面我们来考虑一个问题,jugg追杀敌方单位,准备开大,这时突然冒出来一坨小兵(很常见的情形)。这时jugg是否应该继续放大呢?或者放大后有多大的概率击杀对手呢?
  显然我们将会通过编程来处理这个问题。在此之前,我想介绍关于面向对象的编程(因为我和我们一些同学正在学习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问题,经过恰当的算法改造,可以定义公钥密码体制。
  等等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值