做美赛练习的时候有用到。
嗯。。。练习做完了自己又把代码重新更新了一下。
两个类,一个是MM1类,一个是MMN类,MMN类继承的MM1类,改了一下方法
测试类我又写了一遍,这样就可以对大家所用了,还把代码里的内容注释也蛮详细的。
唯一要注意一点的是,有时候队列时间是-1.1546319456101628E-14这种,其实是0,java在比较的时候有精度会出现一点点小问题,无伤大雅。
要注意的第二点就是,这里如果你给的langda和miu值如果比下来趋近于1的话就代表排队时间很长(就是服务效率慢),这时候你的仿真结果旧跟数据集有关,我做过一个数据集的图像,可供参考。此图的下标1,2,3,4,5打错了,是2,3,4,5,6。
第三点是,算法在仿真数据集为10万时最接近理论值,但是这时候的仿真计算的平均队长时间会比较慢,可能需要等30秒,如果是100万,可能需要等2,3分钟。
源代码如下:
/*
* 完整版排队论仿真模型(MM1与MMN)
* 所需要的其他都可在此基础上更改
* 2018.1.21-2017.1.28陆续修改而成
* from 济民宝宝
*/
import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
class MM1
{
/*
* MM1所具有的函数有以下几个:
* MM1:构造函数,知道langda和miu以及number仿真总人数就可以进行仿真
* Produce:通过负指数分布landa的值产生到达人群的时间间隔,计算得出时间点(泊松分布是产生人群,与到达人群是无关的)
* Leave:通过负指数分布miu的值计算服务时间,队列时间以及等待时间
* Print_need:打印出所需要的平均逗留时间,平均队列时间,平均服务时间与平均队长(这是仿真模型,打印出来的是仿真结果)
* Print_people:打印出编号为x的人的到达时间,队列时间,服务时间与离开时间
* Print_time:打印出时间为T时的队列人数,此时的平均服务时间,到达该窗口的累计人数,当前窗口人数
*/
double in;//in为langda,一秒钟到来了几个人
double out;//out为miu,人离开的速率,完成速率
double[] arrive_time;//到达时间间隔
double[] arrivetime;//顾客到达时间
double[] serve_time;//服务时间
double[] leave_time;//离开时间
double[] wait_time;//等待(在服务窗的总时间)时间
double[] queue_time;//排队时间
MM1(double in,double out,int number)
{
arrive_time=new double[number];//到达时间间隔
arrivetime=new double[number];//顾客到达时间
serve_time=new double[number];//服务时间
leave_time=new double[number];//顾客离开时间
wait_time=new double[number];//等待时间,(在服务窗的总时间)
queue_time=new double[number];//排队时间
this.in=in;
this.out=out;
}
void Produce(int number)
{
for(int i=0;i<number;i++)
{
Random random=new java.util.Random();// 定义随机类
double result=random.nextDouble();// 返回0到1之间的小数
arrive_time[i]=Math.log(result)*-1/in;//负指数分布函数产生到达时间间隔
}
arrivetime[0]=arrive_time[0];
for(int i=1;i<number;i++)
{
arrivetime[i]=arrivetime[i-1]+arrive_time[i];//通过到达时间间隔计算出到达时间
}
}
void Leave(int number)
{
for(int i=0;i<number;i++)
{
Random random=new java.util.Random();// 定义随机类
double result=random.nextDouble();// 返回0到1之间的小数
if(result<0) result*=-1;
serve_time[i]=Math.log(result)*-1/out;
}
leave_time[0]=serve_time[0]+arrivetime[0];
/*
* 离开时间的计算方法:
* 如果前面那个人的离开时间大于我,我的离开时间等于他的离开时间加上我的服务时间
* 如果前面那个人的离开时间小于我,我的离开时间等于我的到达时间加上我的服务时间
*/
for(int i=1;i<number;i++)
{
if(leave_time[i-1]<arrivetime[i])
leave_time[i]=arrivetime[i]+serve_time[i];
else
leave_time[i]=leave_time[i-1]+serve_time[i];
}
for(int i=0;i<number;i++)
{
queue_time[i]=leave_time[i]-arrivetime[i]-serve_time[i];//队列时间的计算
wait_time[i]=leave_time[i]-arrivetime[i];//等待时间的计算
}
}
void Print_people_x(int x)//x是编号
{
System.out.println("第"+x+"个人的数据如下:");
System.out.println("到达时间:"+arrivetime[x]);
System.out.println("队列时间:"+queue_time[x]);
System.out.println("服务时间:"+serve_time[x]);
System.out.println("离开时间:"+leave_time[x]);
}
void Print_need(int x)//x是规模
{
double[] line_wait;//当前队长(包括正在服务的)
double sum_wait=0,sum_queue=0,sum_serve=0,sum_line=0;
for(int i=0;i<x;i++)
{
sum_wait+=wait_time[i];
sum_queue+=queue_time[i];
sum_serve+=serve_time[i];
}
sum_wait/=x;
sum_queue/=x;
sum_serve/=x;
System.out.println("平均逗留时间为:"+sum_wait);
System.out.println("平均队列时间为:"+sum_queue);
System.out.println("平均服务时间为:"+sum_serve);
double last_time=leave_time[x-1];
line_wait=new double[(int)last_time+1];
int time=0;
for(time=0;time<last_time;time++)
{
for(int i=0;i<x;i++)
{
if(time>arrivetime[i]&&time<leave_time[i])
{
line_wait[time]++;
}
}
sum_line+=line_wait[time];
}
sum_line/=time;
System.out.println("平均队长为:"+sum_line);
}
void Print_time(double time,int number)
{
int queue_people=0;
int arrive_people=0;
int leave_people=0;
double servertime=0;
for(int i=0;i<number;i++)
{
if(arrivetime[i]<time&&leave_time[i]>time)
queue_people++;
if(arrivetime[i]<time)
arrive_people++;
if(leave_time[i]<time)
leave_people++;
}
servertime=(time-arrive_time[0])/(leave_people+1);
System.out.println("第"+time+"时间的数据如下:");
System.out.println("此时队列人数有:"+queue_people);
System.out.println("此时的平均服务时间"+servertime);
System.out.println("到达该窗口累计人数:"+arrive_people);
System.out.println("离开此窗口的累计人数:"+leave_people);
}
}
class MMN extends MM1
{
int N;
MMN(double in, double out, int number,int N) {
super(in, out, number);
this.N=N;
// TODO Auto-generated constructor stub
}
void Leave(int number)
{
for(int i=0;i<number;i++)
{
Random random=new java.util.Random();// 定义随机类
double result=random.nextDouble();// 返回0到1之间的小数
if(result<0) result*=-1;
serve_time[i]=Math.log(result)*-1/out;
}
double[]leave_x=new double[N];
for(int i=0;i<N;i++)
{
leave_time[i]=serve_time[i]+arrivetime[i];
leave_x[i]=leave_time[i];
}
for(int i=N;i<number;i++)
{
Arrays.sort(leave_x);
if(leave_x[0]<arrivetime[i])//到达时间比最小的都大时
{
leave_time[i]=arrivetime[i]+serve_time[i];
leave_x[0]=leave_time[i];
}
else if(leave_x[0]>arrivetime[i]){
leave_time[i]=leave_x[0]+serve_time[i];
leave_x[0]=leave_time[i];
}
}
for(int i=0;i<number;i++)
{
queue_time[i]=leave_time[i]-arrivetime[i]-serve_time[i];
wait_time[i]=leave_time[i]-arrivetime[i];
}
}
}
public class Queue {
public static void main(String[] args) {
// TODO Auto-generated method stub
int number=10000;
int flag=0;
while(flag!=5){
System.out.println("济民宝宝为郭阳小姐姐服务:");
System.out.println("本系统提供如下服务:");
System.out.println("1.提供Print_need的测试样例,输入1回车可见(具体代码见Queue类的注释1部分)");
System.out.println("2.提供Print_time的测试样例,输入2回车可见(具体代码见Queue类的注释2部分)");
System.out.println("3.提供Print_people的测试样例,输入3回车可见(具体代码见Queue类的注释3部分)");
System.out.println("4.提供排队论仿真模型(可自行输入输出),输入4回车可见(具体代码见Queue类的注释4部分)");
System.out.println("5.结束");
Scanner in =new Scanner(System.in);
flag=in.nextInt();
/*
* 注释1
* 函数的具体功能见MM1类的注释
* 包含MM1和MM3的Print_need
*/
if(flag==1){
System.out.println("这是一个测试类:测试数据集为10000,langda=0.2,miu=0.5的MM1模型");
MM1 test1=new MM1(0.2,0.5,number);
test1.Produce(number);
test1.Leave(number);
test1.Print_need(number);
System.out.println("\n--------------OvO华丽的分割线QAQ-------------\n");
System.out.println("这是一个测试类:测试数据集为10000,langda=0.2,miu=0.5的MM3模型");
MMN test2=new MMN(0.2,0.5,number,3);
test2.Produce(number);
test2.Leave(number);
test2.Print_need(number);
}
/*
* 注释2
* 包含MM1和MM3的Print_time
*/
if(flag==2){
System.out.println("这是一个测试类:测试数据集为10000,langda=0.2,miu=0.5的MM1模型");
MM1 test1=new MM1(0.2,0.5,number);
double time=100.0;
test1.Produce(number);
test1.Leave(number);
test1.Print_time(time, number);
System.out.println("\n--------------OvO华丽的分割线QAQ-------------\n");
System.out.println("这是一个测试类:测试数据集为10000,langda=0.2,miu=0.5的MM3模型");
MMN test2=new MMN(0.2,0.5,number,3);
test2.Produce(number);
test2.Leave(number);
test2.Print_time(time, number);
}
/*
* 注释3
* 包含MM1和MM3的Print_people
*/
if(flag==3){
System.out.println("这是一个测试类:测试数据集为10000,langda=0.2,miu=0.5的MM1模型");
MM1 test1=new MM1(0.2,0.5,number);
double time=100.0;
test1.Produce(number);
test1.Leave(number);
test1.Print_people_x(100);
System.out.println("\n--------------OvO华丽的分割线QAQ-------------\n");
System.out.println("这是一个测试类:测试数据集为10000,langda=0.2,miu=0.5的MM3模型");
MMN test2=new MMN(0.2,0.5,number,3);
test2.Produce(number);
test2.Leave(number);
test2.Print_time(time, number);
test2.Print_people_x(100);
}
/*
* 注释4
* 自选输入输出
*/
if(flag==4){
System.out.println("请选择你想模拟的是MM1还是MMN");
System.out.println("MM1的话输入1换行,MMN的话输入2换行");
Scanner one=new Scanner(System.in);
int flag_one=one.nextInt();
System.out.println("请输入langda:");
double langda=one.nextDouble();
System.out.println("请输入miu:");
double miu=one.nextDouble();
System.out.println("请输入仿真数量集:");
number=one.nextInt();
System.out.println("请输入你想仿真的方法:");
System.out.println("1.Print_need,输入1回车");
System.out.println("2.Print_people,输入2回车");
System.out.println("3.Print_time,输入3回车");
int method=one.nextInt();
if(flag_one==1){
MM1 test1=new MM1(langda,miu,number);
test1.Produce(number);
test1.Leave(number);
switch(method)
{
case 1:
test1.Print_need(number);
break;
case 2:
System.out.println("请输入你想print_people的编号(不能大于你给的数据集):");
int people=one.nextInt();
test1.Print_people_x(people);
break;
case 3:
System.out.println("请输入你想print_time的时间(不能大于你给的数据集的最后一个人的离开时间):");
double time_one=one.nextDouble();
test1.Print_time(time_one, number);
break;
}
System.out.println("\n--------------OvO华丽的分割线QAQ-------------\n");
}
else if(flag_one==2){
System.out.println("请输入你想输入的窗口数");
int window_number=one.nextInt();
MMN test1=new MMN(langda,miu,number,window_number);
test1.Produce(number);
test1.Leave(number);
switch(method)
{
case 1:
test1.Print_need(number);
break;
case 2:
System.out.println("请输入你想print_people的编号(不能大于你给的数据集):");
int people=one.nextInt();
test1.Print_people_x(people);
break;
case 3:
System.out.println("请输入你想print_time的时间(不能大于你给的数据集的最后一个人的离开时间):");
double time_one=one.nextDouble();
test1.Print_time(time_one, number);
break;
}
}
}
System.out.println("5秒后返回主菜单");
for(int i=0;i<3;i++)
System.out.println();//清屏
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}