精华内容
下载资源
问答
  • 首先需要个数据结构:一个是有序事件链表,一个是队列。 1、事件链表 存储客户事件,包括到达事件和离开事件,其中到达事件的事件类型为0,1号窗口的离开事件类型为1,二号窗口的离开事件类型为2,三号窗口的离开事件...

    队列应用银行排队问题模拟:计算客户的平均停留时间和等待时间以及每个客户的时间信息,两种方法实现

    (上传的资源中有完整的源码哦!可以在CSDN的资源中搜索标题进行下载!)

    第一种类似于买票排队,你总会到队列最短的窗口去排队,但往往会有其他队列办事速度快,队列长度很快变得比你所在队列的还短,但你改变自己的队列去当前较短的队列时,可能没过多久刚刚你在的队列又比你现在所处的队列短了,因为队短不代表等待时间短,你无法预测每个队列你需要等待的时间。所以在该种制度下,不同于买票排队的这种可以随便更换队列的随意性,我们在第一种算法中设定:每到达一个客户将其排在队列最短的队尾,且不管其它队列是否变的更短,甚至已经空闲,该客户也只能在已队列中等待前面的客户办理完业务自己才能办理业务,很明显这种算法效率不是最好的。一是时间利用率不高,而是无法保证先到达的客户的办理业务时间一定比后到达的客户早。

    第二种就相当于是现在银行实行的叫号制度。每个窗口只有一个客户正在办理手续,其它客户都在等待区等待且根据到达事件进行排序,当某个窗口的客户办完业务时,将等待区的最先到达的客户安排到刚空闲下来的窗口去办理业务。这样显然提高了时间利用效率,且先到达的客户办理业务的时间不会晚于后到达的客户。

    一、数据类型

    首先需要两个数据结构:一个是有序事件链表,一个是队列。

    1、事件链表

    存储客户事件,包括到达事件和离开事件,其中到达事件的事件类型为0,1号窗口的离开事件类型为1,二号窗口的离开事件类型为2,三号窗口的离开事件类型为3,四号窗口的离开事件类型为4,由此就可以只使用事件类型就将到达事件和不同窗口的离开事件区分开来.

    (1)链表数据结构:

    1)每个事件项的数据结构为:

    typedef struct{

          int OccurTime;

          int NType;

    }Event,ElemType;

    2)链表的结构:

    typedef struct     LinkList{

          ElemType data;

          struct     LinkList*next;

    }LinkList;

    (2)链表数据操作

    1)创建一个包含头结点空链表,返回指向头结点的指针

    LinkList *InitList_L( );  

    初始化的结果为list指向头结点,list->next指向空。将list返回。

    2)统计链表的元素的个数:并将统计结果返回

    int CountList(LinkList *List_Head ); 

    头结点的下一项即List_Headànext为链表的第一项,若该项为空,则链表长度为0,若该项不为空,从该项开始直达next指向空的项结束的项数的个数即为链表的长度。

    3)判断链表是否为空,若为空,返回1,若不为空,返回0.

    int ListEmpty(LinkList *List_Head );

    头结点指向空,即List_Head指向的结点的后一项List_Head->指向空,或者使用CountList函数统计链表元素个数为0时,链表为空。

    4)将元素e插入到链表中的位置i;即使e成为链表的第i个元素

    intListInsert_L( LinkList *List_Head, int i, ElemType e );

    首先为元素e创建一个指向链表结点指针node_i,并为该链指针分配链表类型LinkList的内存空间,将e的值赋给该指针指向的几点的数据项node_i->data:

    若i为1,将node_i->next指向链表的第一项List_Head->next;将链表的头指针List_Head->next指向指针nodi_i.注意这两个表达式的顺序一定不能反过来。

          node_i->next = List_Head->next;

          List_Head->next = node_i;

    若i不为1,找到第i- 1项,将当前链表的第i-1项的d的next的值赋给node_i的next,将node_i的值赋给第i-1项的next。操作结果是的node_i成为链表的第i项。若i值小于0若当前链表中没有第i-1项,说明要插入的位置为负数或者已经超过了链表长度+1,是一个错误的位置,此时提示错误。

    5)删除链表中的第i个元素,并将该元素的值通过e输出

    intListDelete_L( LinkList *List_Head, int i, ElemType *e )

    若i值小于0若当前链表中没有第i项,说明要删除的元素位置在链表中不存在,是一个错误的位置,此时提示链表错误。

    若i值为1,将List_Head->next指向List_Head->next->next;

    若i不为1,将第i-1项的next,指向第i-1项的next项的next,并将第i项的空间释放。

    6)将链表的元素按OccurTime的正序进行排列,即非递减

    LinkList*Increse( LinkList *List_Head )

    使用冒泡法对该单链表进行排序,需要三个指针,current指向当前项,pre指向当前项的前一项,nex指向当前项的后一项,使用冒泡法使得当前项比他之前的项都小,将当前项大于等于下一项时,交换这两项的位置。对于没有含义的链表排序时为了减少一次交换常常在此处判断条件时不使用等号,而在本程序中等号时也进行交换有特殊的含义:1当前事件的发生时间大于下一个事件的发生时间交换顺序: current->data.OccurTime > nex->data.OccurTime 2当前事件的发生时间等于下一个事件的发生时间且当前事件为到达事件时交换顺序,使得当一个离开事件和一个到达事件的时间相等时,离开事件的排序总是位于到达事件之前,使得之后离开事件先于到达事件被处理: ( current->data.OccurTime == nex->data.OccurTime &&( current->data.NType == 0 ) ) 3当两个离开事件的发生时间相等时,将队列号小的离开事件排在前面,即先处理队列号小的事件: ( current->data.OccurTime == nex->data.OccurTime &&nex->data.NType < current->data.NType && nex->data.NType !=0 )

    2、窗口队列

    假设有n个窗口队列,每次有客户到达时都把客户其分配到排队人数最少且窗口号较小的队列中。

    (1)  队列数据结构

    1)队列项的元素的数据结构

    typedef struct{

          int ArriveTime;  //客户的到达事件

          int Duration;    //一个客户办理事务所需的时间

    }QElemType;

    2)队列链表的数据结构

    typedef structQNode{

          QElemType data;

          struct QNode *next;

    }QNode;

    3)存储指向队列首尾项地址的指针:

    QNode *front指向队列的头结点,QNode*front->next指向队列的第一个有效项,QNode *rear指向队列的最后一项,

    typedef struct{

          QNode *front;

          QNode *rear;     

    }LinkQueue;

    2、数据操作

    1)构建一个包含头结点的空队列

    LinkQueue *InitQueue( )

    需要创建两个指针并为其分配空间:一个为LinkQueue*型,指向队列的第一项(非数据项)即存储       QNode *front和QNode *rear的项,一个为QNode*型,即为指向队列的第二项(非数据项)即头结点。初始化QNode *front和QNode *rear都指向头结点,头结点指向空。

    2)判断队列是否为空

    int QueueEmpty(LinkQueue *Q )

    队列的头结点的Q->front->next指向队列的第一个结点,若该项为NULL;队列即为空。

    3)计算队列的长度

    int QueueLength(LinkQueue *Q )

    即队列中包含的元素个数,不包括队列头即Q->front指向的元素,队列的第一个有效元素是Q->front->next,最后一个元素的next指向NULL

    4)插入元素,由队列的性质决定应该在队尾插入元素

    void InsertQueue(LinkQueue *Q, QElemType e )

    5)删除元素,由队列的性质决定应该在队首删除元素

    voidDeleteQueue( LinkQueue *Q, QElemType *e )

    若队列非空,则进行元素的删除:1第一个结点的数据赋给e指向的单元2将队列的头结点指向第一个结点的下一个结点3释放第一个结点4如果删除队头结点后队列为空,将队尾指针指向头结点。

     

     

     

     

     

     

     

    二、对事件链表和队列的处理

    整个过程需要的变量:

    Event           en;          //当前被处理的事件

    QElemType  customer;  //客户在队列中的记录

    int  TotalTime;  //累计客户逗留时间

    int  WaitTime;   //累计客户等待时间

    int  CustomerNum ;  //累计客户数 

    intCloseTime;    //银行结束时间

    intStartTime;    //银行开始营业时间

    LinkList *ev;         //事件表

    LinkQueue   *q[5];     //4个客户队列,q[i]指向第i号窗口的队列

    int leave_num= 0;   //第leave_num个离开银行的客户

    1、初始化:

    void OpenForDay()

    {

          int i;

          TotalTime = 0;     //初始化累计时间为0

          CustomerNum = 0;   //初始化客户总数为0

          WaitTime = 0;        //初始化等待时间为0

          ev = InitList_L( );  //初始化事件链表为空

          en.OccurTime = 0;    //设定第一个客户到达事件

          en.NType = 0;

          OrderInsert( ev, en ); //将第一个客户到达事件插入事件表

          for( i = 1; i <= 4; i++ )    //初始化窗口队列

                 q[ i ] = InitQueue();

    }

    2、产生随机数的函数:

    第一个参数代表当前客户的办理业务所需的时间,第二个参数代表下一个客户与当前客户的到达时间差。

    void Random(int *duration, int *intertime ); 

    {

          srand( (unsigned)time( NULL ) );  //用时间作为种子对随机数进行操作

          *duration = rand()%30 + 1;  //任何一个客户的办理业务时间在1-30之间

          *intertime = rand()%5+1;   //任何两个客户到达的时间间隔不超过5分钟,1-5

          Sleep(1000);  //由于随机函数的产生机制导致在一秒以内产生的随机数都是相同的,因此在一次使用Random时需要进行延时

    }

    3、获得当前客户插入队列的队列号:

    int Minium( int num1, int num2, int num3, int num4 );

    比较四个数中的最小值,若第i个参数最小,则返回i,参数相等的情况下返回序号较小的参数的序号。调用时四个参数分别为第一个队列的长度,第二个队列的长度,第三个队列的长度,第四个队列的长度。调用结果是返回长度最短且队列号较小的队列的队列号。

    4、模拟函数:初始化数据,包含主循环,打印结果

    删除当前事件表中的第一个事件,并将该事件的参数赋给当前事件en:如果当前事件en为到达事件即en.NType == 0,则调用到达事件处理函数,否则当前事件为离开事件,则调用离开事件处理函数。参数CloseTime为对应的营业总时间的分钟数:即为(关门时间 – 开门时间) * 60

    void Bank_Simulation( int CloseTime )

    {

    OpenForDay();

    while(!ListEmpty( ev ) ){

          ListDelete_L(  ev, 1, &en );               

    if( en.NType== 0 ){  

                 CustomerArrived( );  

          }

          else

                 CustomerDepature();

    }

    printf("客户的平均停留时间是: %f minutes\n", (float) TotalTime/CustomerNum );

    printf("客户的平均业务办理时间时间是: %f minutes\n", (float) TotalTime/CustomerNum -WaitTime/CustomerNum );

    printf("客户的平均等待为其办理业务的时间是: %f minutes\n", (float) WaitTime/CustomerNum );      }

    5、到达事件处理函数:

    voidCustomerArrived(  );

    1)到达客户总数加1,利用随机数生成函数生成下一个到达事件,若到达事件的时间在CloseTime之前则用OrderInsert函数将其插入到事件表中。

    2)生成当前到达事件的队列项customer并利用Minium函数找到合适的队列号用InsertQueue函数将其插入到队列中。

    3)只有当当前到达事件位于队列的队首时,才在此函数中为其生成离开事件并用OrderInsert函数将其插入到事件表中,此时对应客户到达后无需等待即可办理业务的情况。因此对应的离开事件的发生时间为到达事件与办理业务时间之和:first_leave.OccurTime = customer.ArriveTime + customer.Duration;离开事件的类型为对应的队列号first_leave.NType = i。若当前到达事件不位于队首,则其需要等待位于他前面的最后一个客户离开后才能办理业务,其离开事件应该等于前一个客户的离开事件加上当前到达的客户办理业务的时间,因此应该在他前面的最后一个客户的离开事件处理函数中为其生成对应的离开事件。

    基本思想就是只为位于队首的队列项生成离开事件。

    6、离开事件处理函数:

    voidCustomerDepature(  )

    1)利用当前事件的en.NType找到当前事件对应的队列项并将其在队列中除除并把该队列项的参数赋给customer。表示该客户已经办理完业务。

    2)如果删除当前事件对应的队列项后,其所在的队列不为空,则为当前队首的队列项生成对应的离开事件。此时队首的队列项对应的离开事件的发生时间为当前离开事件的发生时间与当前位于队首的队列项的办理业务时间之和:depature.OccurTime = en.OccurTime +q[i]->front->next->data.Duration; 队首的队列项对应的离开事件的类型即为当前离开事件的事件类型。depature.NType = i。

    ( 与基本思想:只为位于队首的队列项生成离开事件对应)

    3)离开序列号+1;

    计算当前离开事件的并将其转换为24小时制:

    到达时间:即为custome.ArriveTime

    办理业务时间:即为customer.Duration

    离开时间:即为当前事件的发生时间:en.OccurTime

    停留时间:离开时间 – 到达时间,en.OccurTime--custome.ArriveTime

    等待时间:停留时间 –办理业务时间en.OccurTime--custome.ArriveTime--customer.Duration

    计算该离开事件发生后累计的总停留时间:

    计算该离开事件发生后累计的总的等待时间

    4)打印离开客户的:离开序列号,到达事件,业务窗口,办理业务时间,离开事件,停留时间,等待时间

    7、将产生的事件插入按事件发生时间的顺序插入到事件表中

    voidOrderInsert( LinkList *eventlist, Event cur_en )

    {

    if(ListEmpty( eventlist) ){           

    ListInsert_L(eventlist, 1, cur_en );

    }

    else{           

    ListInsert_L(eventlist, 1, cur_en ); 

          Increse( eventlist ); 

    }

    }

    当事件链表为空时,将第一个事件直接插入到第一个位置,当事件链表不为空时,先将事件插入到事件表的第一个位置然后将事件表按非递减的顺序排序。

    三、处理过程

    1、初始状态


    事件表只有一项{0,0}队列为空。

    2、第一步:处理第一个客户到达事件

    (1)将第一个客户的到达事件从事件表中删除,事件表为空

    (2)生成第二个客户到达事件,并将此事件有序插入到事件表中

    (3)生成第一个客户到达事件的队列项,并为其分配队列窗口1,此时为其分配的队列窗口长度为1,即第一个客户到达之后无序等待即可办理业务,故

    (4)为第一个客户生成离开事件,离开事件的发生时间即为到达事件的发生时间与半夜业务时间之和。将第一个客户的离开事件有序插入到事件表中。

    此时,事件表中有两项,一项为第一个客户的离开事件,一项为第二个客户的到达事件。事件表中项数最多的情况是只有5项:一个到达事件项和四个离开事件项,且这四个离开事件对应的到达事件的发生事件在该到达事件项的发生时间之前。

    3、第二步:

    (1)第一个客户的离开事件位于事件表的第一项

    (1)将第一个客户的离开事件从事件表中删除并将事件参数赋给当前事件en,利用第一个客户的离开事件的en.NType找到其对应的队列项1并将其在队列中删除并把该队列项的参数赋给customer。表示第一个客户已经办理完业务。此时第一个队列为空。没有需要生成的离开事件。

    3)离开序列号+1;

    计算当前离开事件的并将其转换为24小时制:

    到达时间:即为custome.ArriveTime

    办理业务时间:即为customer.Duration

    离开时间:即为当前事件的发生时间:en.OccurTime

    停留时间:离开时间 – 到达时间,en.OccurTime--custome.ArriveTime

    等待时间:停留时间 –办理业务时间en.OccurTime--custome.ArriveTime--customer.Duration

    计算该离开事件发生后累计的总停留时间:

    计算该离开事件发生后累计的总的等待时间

    4)打印第一个客户离开的:

    离开序列号,到达时间,业务窗口,办理业务时间,离开时间, 停留时间,  等待时间

    1     arr_h:arr_m   1       dur_m   en.NType      stop     wait

    (2)第二个客户的到达事件位于事件表的第一项

    (1)将第二个客户的到达事件从事件表中删除,事件表为空

    (2)生成第三个客户到达事件,并将此事件有序插入到事件表中

    (3)生成第二个客户到达事件的队列项,并为其分配队列窗口2,此时为其分配的队列窗口长度为1,即第一个客户到达之后无序等待即可办理业务,故

    (4)为第二个客户生成离开事件,离开事件的发生时间即为到达事件的发生时间与半夜业务时间之和。将第二个客户的离开事件有序插入到事件表中。

    此时,事件表中有三项,一项为第一个客户的离开事件,一项为第二个客户的离开事件,一项为第三个客户的到达事件。

    4、后续

    执行过程与第二步相似:但值得一提的是事件表中项数最多的情况是只有5项:一个到达事件项和四个离开事件项,且这四个离开事件对应的到达事件的发生事件在该到达事件项的发生时间之前。因为此时四个离开事件对应的队列项已经占据了各个队列项的队首。

    (1)此时若队首为客户到达事件时:

    在事件表中删除该事件并将该事件的参数赋给当前事件en。用到达事件处理函数对en进行处理,在处理时只进行以下操作:

    1)利用随机数生成函数生成下一个到达事件并用OrderInsert函数将其有序插入到事件表中。

    2)生成当前到达事件的队列项customer并利用Minium函数找到合适的队列号用InsertQueue函数将其插入到队列中。

    (2)此时若队首为客户离开事件时:

    在事件表中删除该离开事件并将该事件的参数赋给当前事件e。用离开事件处理函数对en进行处理,在处理时进行以下操作:

    1)利用当前事件的en.NType找到当前事件对应的队列项并将其在队列中除除并把该队列项的参数赋给customer。表示该客户已经办理完业务。

    2)如果删除当前事件对应的队列项后,

    1若其所在的队列不为空,则为当前队首的队列项生成对应的离开事件。

    2若若其所在的队列为空,则没有需要生成的离开事件。

    3)离开序列号+1;

    计算当前离开事件的并将其转换为24小时制:

    到达时间:即为custome.ArriveTime

    办理业务时间:即为customer.Duration

    离开时间:即为当前事件的发生时间:en.OccurTime

    停留时间:离开时间 – 到达时间,en.OccurTime--custome.ArriveTime

    等待时间:停留时间 –办理业务时间en.OccurTime--custome.ArriveTime--customer.Duration

    计算该离开事件发生后累计的总停留时间:

    计算该离开事件发生后累计的总的等待时间

    4)打印离开客户的:离开序列号,到达事件,业务窗口,办理业务时间,离开事件,停留时间,等待时间

    5、运行结果实例:



    四、改进算法

    此种算法对应的情况是:每次客户一到达,就为其分配队伍最短的窗口号,此时该客户只能在该窗口办理业务,但队列最短并不一定等待时间最短,因为可能该队列前面的客户办理业务所需的时间都很长,而其他队列虽然长,但办理业务的时间短,有可能其它窗口已经空闲了,但该客户也只能在该窗口等待为其办理业务。这显然不是最优的算法。造成这种现象的原因就是为在客户一到达时就为其分配窗口。更优的算法应该是现在银行的排队机制。每个窗口只有一个客户在办理业务,前1,2,3,4个到达的客户在办理业务,后面到达的客户先不为其分配窗口,所有的人都在等待区等待,当某一个窗口的客户离开时,将等待区的最先到达的客户分配到该窗口去办理业务。此时就需要两个事件表:一个到达事件表,一个离开事件表。

    (红色加粗字体的地方为与之前的算法不同的地方)

    1、数据项

    Event           en;          //事件

    QElemType  customer;  //当前事件对应的队列项,客户记录

    int  TotalTime; //累计客户逗留时间

    int  WaitTime; //累计客户等待时间

    int  CustomerNum ; //累计客户数      

    int CloseTime;//营业结束时间

    int StartTime;//营业开始时间

    LinkList       *ev_arrive;      //到达事件表,比之前的算法多一个到达事件表

    LinkList *ev_leave;          //离开事件表

    LinkQueue   *q[5];     //4个客户队列,q[i]指向第i号窗口的队列

    int leave_num =0;   //第leave_num个离开银行的客户

    Event newestleave_en;  //最新删除的离开事件项

    2、初始化

    void OpenForDay()

    {

     

          int i;

          TotalTime = 0;     //初始化累计时间为0

          CustomerNum = 0;   //初始化客户总数为0

          WaitTime = 0;            //初始等待时间为0

          ev_arrive= InitList_L( );  //初始化到达事件链表为空

          ev_leave = InitList_L( );  //初始化离开事件链表为空

          en.OccurTime = 0;    //设定第一个客户到达事件

          en.NType = 0;     //0表示到达事件

          OrderInsert( ev_arrive, en ); //将第一个客户到达事件插入事件表

         

          for( i = 1; i <= 4; i++ )    //初始化窗口队列

                 q[ i ] = InitQueue();

    }

    3、随机数生成函数:

    分别生成到达客户的时间间隔和业务办理时间

    voidRandom_duration( int *duration )

    {

          srand( (unsigned)time( NULL ) );  //用时间作为种子对随机数进行操作

          *duration = rand()%30 + 1;  //任何一个客户的办理业务时间在1-30之间

          Sleep(1000);

    }

    voidRandom_intertime( int *intertime  )

    {

          srand( (unsigned)time( NULL ) ); //用时间作为种子对随机数进行操作

          *intertime = rand()%5+1;    //客户到达的时间间隔不超过5分钟,1-5

          Sleep(1000);  

    }

    4、将事件cur_en有序的插入事件链表eventlist中

    voidOrderInsert( LinkList *eventlist, Event cur_en ) 

    {

          if( ListEmpty( eventlist) ){              

    ListInsert_L( eventlist, 1, cur_en );

          }

          else{                                 

                 ListInsert_L( eventlist, 1, cur_en); 

                 Increse( eventlist );

          }

    }

    当事件表为空时,将第一个事件直接插入到第一个位置,当事件表不为空时,先将事件插入到事件表的第一个位置然后将事件表按非递减的顺序排序。

    将离开事件插入到离开事件表时,若两个离开事件的发生时间相等,应该将对应窗口号较小的离开事件排在前面。因此Increase函数中判断两个事件是否需要交换顺序的条件应为:

    current->data.OccurTime> nex->data.OccurTime || ( current->data.OccurTime ==nex->data.OccurTime && current->data.NType >nex->data.NType))

    5、模拟函数:

    voidBank_Simulation( int CloseTime )

    (1)对各项数据进行初始化

    (2)当离开事件表和到达事件表都不为空时结束循环,否则

    (3)当离开事件表为空时,则对到达事件列表的第一个到达事件进行处理:删除到达事件表的第一项,并将该项的参数赋给当前事件en,调用到达时间函数对en进行处理。

    (只有第一个到达事件到达时或者离开事件表的所有离开事件的发生时间都小于到达事件表的第一个到达事件的到达时间时,离开事件表才为空)

    (4)当到达事件表为空时,说明已经到了银行的最晚营业时间,没有客户再到达了,此时需要对离开事件表进行处理。删除当前离开事件表的第一项,并将对应的参数赋给当前事件en和最近离开事件,调用离开事件处理函数对en进行处理。               

    (5)当两个事件表都不为空时:

    1)若离开事件表的第一项的发生事件小于等于到达事件的第一项的发生时间,则删除当前离开事件表的第一项,并将对应的参数赋给当前事件en和最近离开事件,调用离开事件处理函数对en进行处理。 

    2)若离开事件表的第一项的发生事件大于到达事件的第一项的发生时间:

          1如果当前离开事件表的项数大于等:于4,说明没有空闲的窗口,需要先对离开事件表进行处理以便得到空闲的窗口分配给到达事件表的第一项。删除当前离开事件表的第一项,并将对应的参数赋给当前事件en和最近离开事件,调用离开事件处理函数对en进行处理。      

          2如果当前离开事件表的项数小于4,说明有空闲窗口,此时才可以对到达事件的第一项进行处理,除当前到达事件表中的第一项并将删除结果赋给当前事件项en,调用到达时间处理函数。

    (6)循环结束后打印最终结果

    注:在上述几步条件判断的过程中使得窗口有空闲时才对到达事件进行处理并为其分配窗口和产生离开事件。若窗口没有空闲即使到达事件的第一项的发生事件小于离开事件的第一项的发生时间也需要先对离开事件进行处理。因此在处理到达事件时也就代表窗口有空闲。

    5、到达事件处理函数:

    (1)到达客户总数加1,

    (2)利用随机数生成函数生成下个客户的到达事件与当前客户的到达事件的时间间隔。生成下个客户的到达事件:下个客户的到达时间为:next.OccurTime = en.OccurTime + intertime; 事件类型为next.NType= 0。若到达事件的时间在CloseTime之前则用OrderInsert函数将其插入到达事件表。

    (3)分配窗口号,此时必有空闲的窗口:因为只有当有空闲窗口时才会调用到达事件处理函数。

    1)计算当前客户的队列项并将队列项分配到对应空闲的窗口队列

    2)比较1)中为当前客户分配的窗口号与最近离开的客户的窗口号进行比较:若相等说明处最近离开的客户办理业务的窗口外其他窗口都没有空闲,若不等说明其他窗口有空闲。

    1若相等当前客户的到达时间小于最近离开的客户的离开时间,则当前客户的离开事件的发生时间为:最近离开的客户的离开事件与当前客户办理业务所需时间之和。

    2若相等且当前客户的到达时间大于最近离开的呼呼的离开事件,则当前客户的离开事件的发生时间为:当前客户的到达时间与办理业务时间之和。

    3若不等则当前客户的离开事件的发生时间为:当前客户的到达时间与办理业务时间之和。

    4当前客户的离开事件的事件类型等于为其分配的窗口号。将当前客户的离开事件有序的插入到离开事件表中。

    6、离开事件处理函数

    voidCustomerDepature(  )

    (1)删除当前离开事件对应的队列项,将该队列项删除并赋值给customer

    (2)离开序列号加1

    (3)计算离开事件的各项数据并打印

    当前离开事件对应的到达时间即为队列项customer.ArrivrTime

    当前离开客户的业务办理时间即为对应的队列项customer.Duration

    当前离开客户的离开时间即为当前离开事件的en的发生时间en.OccurTime

    当前离开客户的停留时间即为离开事件的发生时间减去到达时间:

    ( en.OccurTime - customer.ArriveTime )

    当前离开的客户等待为其办理业务的时间即为停留时间减去办理业务的时间:en.OccurTime - customer.ArriveTime - customer.Duration

    (4)计算当前客户离开时客户停留的总时间和客户等待为其办理业务的总时间。

    7、运行结果:


    平均等待时间和平均停留时间减少了20分钟左右。


    展开全文
  • 四分位数的两种计算方法

    千次阅读 2020-12-29 20:16:54
    关于四分位数的两种求法 在数据导论课上,我们学习了如何求解四分位数的方法,其实操作起来也不难先用 (n+1) / 4 * i 计算出四分位数的位置,再求出该位置上的数的值即可。如一组数据 【1,3,6,8,10】 根据公式先...

    关于四分位数的两种求法

    在数据导论课上,我们学习了如何求解四分位数的方法,其实操作起来也不难先用 (n+1) / 4 * i 计算出四分位数的位置,再求出该位置上的数的值即可。如一组数据 【1,3,6,8,10】 根据公式先求出第一个四分位数的位置1.5,然后再用 1 * 0.5 + 3 * 0.5 = 2 得出第一个四分位数为2,如此类推可以得到Q2=6,Q3=9,如此一来,我们便掌握了四分位数的求法。

    课后老师布置了作业,给出一组数据,要求求出四分位数。数据如下【35,55,60,67,70,74,75,75,76,78,78,80,83,87,88,90,93】,我一看这还不简单吗,(17+1)/4=4.5,直接用67 * 0.5+70 * 0.5=68.5,同样的,Q3=85。解决问题了。

    但老师后面还给了一段python的程序要我们运行,于是我把程序运行了下却发现结果是不同的,得出的结果是70和83。一开始我以为自己算错了,但这么简单的数字运算,几次运算的结果都是相同的,应该不是计算的问题,但是我怕是犯了什么低级错误,所以我用excel表格里面的函数模块来计算四分位数,发现结果仍然是70和83,难道是算法的问题,于是我上网重新搜索了四分位数的计算方法,大部分都是这么算的,似乎算法也没有什么问题。于是我寻寻觅觅,在其中一个网页上发现了一种不同的计算方法其公式是:(n-1)/4i+1,通过这种方法计算上面的那一组数据(17-1)/41+1正好是5,而第五个位置正好是70,同样的,用这种方法计算Q3=83,也与程序结果相符的。我想应该是我们用的求法和程序的求法不同导致了结果的不同。后来我发现在excel表格中有两种不同的求四分位数的函数其一是:QUARTILE.EXC,以这个函数算出来的结果就是68.5和85,而另一个函数QUARTILE算出来的结果就是70和83。真相大白了。

    但让我费解的是明明确定求四分位数,为什么会有两种求法并且结果还会有所差异呢?

    这得从其求法的原理说起,方法1是把数据看成一个个块,一组数据就可以看成是一条带子,这条带子的中间位置就是第二个四分位数的位置,再除以2,就是Q1 或Q3的位置。而方法的话则是把数字看成是一个个点,这些点分布再一条绳子上,绳子的四分之一处就是四分位数的位置。

    求法的原理不同归根结底是两种四分位数的含义不同,四分位数的定义是:“四分位数把数据分成了左右两份,使左边含有25%的数据,右边含有75%的数据,但四分位数位置上这个数到底是算在哪一边似乎都是可行的,方法一是放在25%这一边的,方法二则是放在中间的,使得其前有25%的数据,其后有75%的数据。

    这就是为何四分位数有两种计算方法

    展开全文
  • 多分类场景下的性能度量指标有:准确率、精确...根据多分类下的混淆矩阵,计算macro“宏平均”和micro“微平均两种形式的查准率查全率的计算公式。当混淆矩阵不平衡时(长方形),如何计算微平均下的查准率和查全率。

    分类算法的性能度量是指对分类算法的泛化性能评估,是衡量模型泛化能力的评价标准,泛化性能评价指标可以定量的评价泛化性能优劣。

    常用的一些指标有准确率(Accuracy)、精确率(Precision)、召回率(Recall)、F1-score等。

    下面以二分类问题为例介绍分类算法的性能评价指标。

    二分类性能度量

    我们以西瓜好坏的预测为例,正例:好瓜,反例:坏瓜,对于二分类问题,将样本依据真实的类别和分类器的预测列别进行组合,可分为四种情况:

    TP、FN、FP、TN的读法

    TP(true positive):正确地预测为正例的样本数,将正例预测为正例的样本数;

    FN(false negative):错误地预测为反例的样本数,将正例预测为反例的样本数;

    FP(false positive):错误地预测为正例的样本数,将反例预测为正例的样本数;

    TN(true nagative):正确地预测为反例的样本数,将反例预测为反例的样本数;

    true、false表示预测结果的正确与错误,positive、negative表示实际样本的正例与反例。

    错误率和准确率

    错误率(error rate)是指分类错误的样本数占总样本数的比例;

    errorrate=FN+FPTP+FN+FP+TNerror rate = \frac{FN+FP}{TP+FN+FP+TN}

    准确率是指分类正确的样本数占总样本数的比例。

    accuarcy=TP+TNTP+FN+FP+TNaccuarcy = \frac{TP+TN}{TP+FN+FP+TN}

    查准率

    查准率,也叫精确率(precision),预测命中率等。

    表示所有预测为正例的样本中实际是正例的样本数所占的比例,即正确地预测为正例的样本数÷\div预测为正例的样本总数。

    P=TPTP+FPP = \frac{TP}{TP + FP}

    查全率

    查全率,也叫召回率(recall),预测召回率等。

    表示所有实际正例的样本中预测为正例的样本数所占的比例,即正确地预测为正例的样本数÷\div实际正例的样本总数。

    R=TPTP+FNR = \frac{TP}{TP + FN}

    F1-score

    F1-score是基于查准率与查全率的调和平均(harmonic mean)。

    F1=2×P×RP+RF1 = \frac{2 \times P \times R}{P + R}

    多分类性能度量

    假设多分类类别数目为nn

    多分类场景下度量查准率与查全率时,考虑将多分类问题简化为二分类问题即可得到类似二分类的混淆矩阵。

    例如,当我们观察某个类别ii时,将其余的nin-i个类别看作是二分类中的反例,这样我们就可以计算出类别iiTPiFNiFPiTNiTP_i、FN_i、FP_i、TN_i

    通过上述的方法,我们可以得到nn个二分类的混淆矩阵,对nn个二分类的混淆矩阵进行计算可得到准确率、错误率、查准率和查全率。

    错误率和准确率

    这里考虑多分类时,由于我们之前定义的混淆矩阵是按照nn个二分类进行计算,得到每个类别iiTPiFNiFPiTNiTP_i、FN_i、FP_i、TN_i

    举个例子,有四个分类,混淆矩阵为:

    预测分类 1 2 3 4
    1 T1P1T_1P_1 F1P2F_1P_2 F1P3F_1P_3 F1P4F_1P_4
    2 F2P1F_2P_1 T2P2T_2P_2 F2P3F_2P_3 F2P4F_2P_4
    3 F3P1F_3P_1 F3P2F_3P_2 T3P3T_3P_3 F3P4F_3P_4
    4 F4P1F_4P_1 F4P2F_4P_2 F4P3F_4P_3 T4P4T_4P_4

    其中,FjPiF_jP_i可读作将真实的分类jj错误地预测为分类ii,则

    FPi=j=1,ji4FjPiFP_i = \displaystyle \sum_{j=1,\: j\neq i}^4{F_jP_i}

    TPi=TiPiTP_i=T_iP_i读作将真实的分类ii正确地预测为分类ii

    FNiFN_i读作:错误地将真实分类ii预测为其他分类;FNi=j=1,jinFiPjFN_i = \displaystyle \sum_{j=1,\: j\neq i}^n{F_iP_j}

    此时,如果混淆矩阵为正方形时,样本总数为i=1nTPi+FPi\displaystyle \sum_{i=1}^n{TP_i+FP_i},也等于i=1nTPi+FNi\displaystyle \sum_{i=1}^n{TP_i+FN_i}

    而不再是类似二分类中的i=1nTPi+FNi+FPi+TNi\displaystyle \sum_{i=1}^n{TP_i+FN_i+FP_i+TN_i}的累加和。

    从上图混淆矩阵中可以直观地看出,TPiTP_i为按列统计预测错误的样本数,FNiFN_i为按行统计预测错误的样本数。

    错误率(error rate)是指分类错误的样本数占总样本数的比例;

    errorrate=i=1nFPii=1nTPi+FPierror rate = \frac{\displaystyle \sum_{i=1}^n{FP_i}}{\displaystyle \sum_{i=1}^n{TP_i+FP_i}}

    准确率是指分类正确的样本数占总样本数的比例。

    accuarcy=i=1nTPii=1nTPi+FPiaccuarcy = \frac{\displaystyle \sum_{i=1}^n TP_i}{\displaystyle \sum_{i=1}^n{TP_i+FP_i}}

    macro度量

    根据计算方法的不同,可将多分类的度量分为macro方式和micro方式。

    先在nn个二分类的混淆矩阵上计算出查准率和查全率,再计算平均查准率和平均查全率。

    可见,macro是基于类别的加权平均,每个类别的权重相同。

    macro查准率

    正确地预测为类别ii的样本数÷\div预测为类别ii的样本总数(预测命中率)

    Pi=TPiTPi+FPiP_i = \frac{TP_i}{TP_i + FP_i}

    则,macroP=1ni=1nPimacro-P =\frac{1}{n} \displaystyle \sum_{i=1}^nP_i

    其中,TPiTP_i读作:正确地预测为分类ii

    FPiFP_i读作:错误地将其他分类预测为分类ii的样本数。

    且,TPi=TiPiTP_i = {T_iP_i}FPi=j=1,jinFjPiFP_i = \displaystyle \sum_{j=1,\: j\neq i}^n{F_jP_i}

    TiPiT_iP_i读作:正确地将真实分类ii预测为分类ii

    FjPiF_jP_i读作:错误地将真实分类jj预测为分类ii

    macro查全率

    正确地预测为类别ii的样本数÷\div类别ii的实际样本数(预测召回率)

    Ri=TPiTPi+FNiR_i = \frac{TP_i}{TP_i + FN_i}

    则,macroR=1ni=1nRimacro-R =\frac{1}{n} \displaystyle \sum_{i=1}^nR_i

    其中,FNiFN_i读作:错误地将真实分类ii预测为其他分类。

    且,FNi=j=1,jinFiPjFN_i = \displaystyle \sum_{j=1,\: j\neq i}^n{F_iP_j}

    FiPjF_iP_j读作:错误地将真实分类ii预测为分类jj

    macroF1-score

    macroF1-score有两种计算公式:

    1. 基于macroPmacro-PmacroRmacro-R的按照公式直接计算:

      macroF1=2×macroP×macroRmacroP+macroRmacro-F1 = \frac{2 \times macro-P \times macro-R}{macro-P + macro-R}

    2. 基于PiP_iRiR_iF1iF1_i的平均:

      F1i=2×Pi×RiPi+RiF1_i = \frac{2 \times P_i \times R_i}{P_i + R_i}

      macroF1=1n×i=1nF1imacro-F1 = \frac{1}{n} \times \displaystyle \sum_{i=1}^n{F1_i}

    micro度量

    先将nn个二分类的混淆矩阵的四个元素进行平均,得到TP\overline{TP}FN\overline{FN}FP\overline{FP}TN\overline{TN},然后再计算平均查准率和平均查全率。

    可见,micro是基于样本数的加权平均,每个样本的权重相同。

    TP=1ni=1nTPi\overline{TP} =\frac{1}{n} \displaystyle \sum_{i=1}^n TP_i

    FP=1ni=1nFPi\overline{FP} =\frac{1}{n} \displaystyle \sum_{i=1}^n FP_i

    FN=1ni=1nFNi\overline{FN} =\frac{1}{n} \displaystyle \sum_{i=1}^n FN_i

    TN=1ni=1nTNi\overline{TN} =\frac{1}{n} \displaystyle \sum_{i=1}^n TN_i

    micro查准率

    microP=TPTP+FP=i=1nTPii=1nTPi+i=1nFPimicro-P = \frac{\overline{TP}}{\overline{TP} + \overline{FP}} = \frac{\displaystyle \sum_{i=1}^n TP_i}{\displaystyle \sum_{i=1}^n TP_i + \sum_{i=1}^n FP_i}

    micro查全率

    microR=TPTP+FN=i=1nTPii=1nTPi+i=1nFNimicro-R = \frac{\overline{TP}}{\overline{TP} + \overline{FN}} = \frac{\displaystyle \sum_{i=1}^n TP_i}{\displaystyle \sum_{i=1}^n TP_i + \sum_{i=1}^n FN_i}

    microF1-score

    microF1=2×microP×microRmicroP+microRmicro-F1 = \frac{2 \times micro-P \times micro-R}{micro-P + micro-R}

    根据之前的分析,如果混淆矩阵为正方形时,样本总数为i=1nTPi+FPi\displaystyle \sum_{i=1}^n{TP_i+FP_i},也等于i=1nTPi+FNi\displaystyle \sum_{i=1}^n{TP_i+FN_i}

    因此,当混淆矩阵为正方形时,有microP=microR=microF1micro-P=micro-R=micro-F1

    当混淆矩阵为长方形时,虽然i=1nFPi=i=1nFNi\displaystyle \sum_{i=1}^n{FP_i} = \displaystyle \sum_{i=1}^n{FN_i},这里nn为支持的分类数。

    但是我们在计算查准率和查全率的分母时,分类数应该取真实样本的分类数nn',此时nnn \neq n'

    举个混淆矩阵为长方形的例子,支持三个分类,混淆矩阵为:

    预测分类 1 2 3
    真实 1 T1P1T_1P_1=27 F1P2F_1P_2=1 F1P3F_1P_3=17
    分类 2 F2P1F_2P_1=0 T2P2T_2P_2=8 F2P3F_2P_3=5

    这里,n=3n=3n=2n'=2

    TP=1ni=1nTPi=1n(T1P1+T2P2)=1n×(27+8)=1n×35\overline{TP} = \frac{1}{n} \displaystyle \sum_{i=1}^{n'}{TP_i} = \frac{1}{n} (T_1P_1+T_2P_2)=\frac{1}{n} \times(27+8)=\frac{1}{n} \times 35

    FP=1ni=1nFPi=1n(F2P1+F1P2)=1n×(0+1)=1n×1\overline{FP} = \frac{1}{n} \displaystyle \sum_{i=1}^{n'}{FP_i} = \frac{1}{n} (F_2P_1+F_1P_2)=\frac{1}{n} \times(0+1)=\frac{1}{n}\times 1

    FN=1ni=1nFNi=1n(F1P2+F1P3+F2P1+F2P3)=1n×(1+17+0+5)=1n×23\overline{FN} = \frac{1}{n} \displaystyle \sum_{i=1}^{n'}{FN_i} = \frac{1}{n} (F_1P_2+F_1P_3+F_2P_1+F_2P_3)=\frac{1}{n} \times (1+17+0+5) = \frac{1}{n} \times 23

    microP=TPTP+FP=i=1nTPii=1nTPi+i=1nFPi=3535+1=0.972222222micro-P = \frac{\overline{TP}}{\overline{TP} + \overline{FP}} = \frac{\displaystyle \sum_{i=1}^{n'} TP_i}{\displaystyle \sum_{i=1}^{n'} TP_i + \sum_{i=1}^{n'} FP_i} = \frac{35}{35+1}=0.972222222

    microR=TPTP+FN=i=1nTPii=1nTPi+i=1nFNi=3535+23=0.603448276micro-R = \frac{\overline{TP}}{\overline{TP} + \overline{FN}} = \frac{\displaystyle \sum_{i=1}^{n'} TP_i}{\displaystyle \sum_{i=1}^{n'} TP_i + \sum_{i=1}^{n'} FN_i}=\frac{35}{35+23}=0.603448276

    展开全文
  • 是一不稳定的排序方法。基本思想是分治法,这位大大的http://blog.csdn.net/morewindows/article/details/6684558 讲的非常清楚了,分治法+挖坑法,我就不多说了。就是以某个数为参照,使得左边的都小于他,右边...

    首先简单谈下快速排序的特点,时间复杂度O(nLog n),最差时间复杂度O(n^2),平均时间O(nLog n).因为用到了函数栈,空间复杂度为O(lg n),最差为O(n).是一种不稳定的排序方法。基本思想是分治法,这位大大的http://blog.csdn.net/morewindows/article/details/6684558 讲的非常清楚了,分治法+挖坑法,我就不多说了。就是以某个数为参照,使得左边的都小于他,右边的数都大于他。然后对他的左右两个区间采取同样的方法进行递归。

    就其整体实现而言,有两大种思路,一是双边扫描,二是单边扫描。下面分别来上程序:

    一、双边扫描

    双边扫描是谭浩强书中的方法,个人觉得比下面的单边扫描更好理解,也是博文里采用的方法。下面看程序:

    void quickSort1(int* x, int l, int r){
    
    	if(l < r){
    		int i = l, j = r, key = x[l];
    		while(i < j){
    			while( i < j && x[j] >= key){
    				j--;
    			}
    			if(i < j){
    				x[i++] = x[j];
    			}
    			while(i < j && x[i] <= key){
    				i++;
    			}
    			if(i < j){
    				x[j--] = x[i];
    			}
    		}
    		cout<<"i = " <<i<<" j = "<<j<<endl;
    		x[i] = key;
    		quickSort1(x, l, i-1);
    		quickSort1(x, i+1, r);
    	}
    
    }

    双边扫描非常直观,首先进到程序里判断是否l<r,当满足条件才进去。这也是用递归的一个必要条件,一定要让函数有尽头,有边界。然后进入大while循环,接着进入小while循环,先从右边找,只要满足数字大于key就一直让j往左移。直到第一个不满足条件的,就是第一个小于key的数跳出while循环,将它放在左边挖的“坑”上。同时让坑的索引+1,接着从左边开始扫描,找到第一个大于key的数,再将它填到右边的坑上。右边的坑索引-1,接着再从右边扫描。直到最后跳出大while循环,此时i = j。也就是完成了一次快速排序的扫描。之后将最初的key放到x[i],其实放到x[j]也是一样的。因为i等于j么,此时!然后进行递归,对区间[l, i - 1], [i+1, r]进行同样的操作。

    双边排序的要点: 1、最初的if一定要有,这是最后递归出来的标志位。2,为了找到一个数使它的左边都大于它,右边都小于它,要多次循环,这个循环就是大while循环。3、双边排序不需要swap,即无需交换。


    二、单边扫描

    上面的双边排序,出来一次大while循环,要从两边进行多次。单边扫描,则只需从左走到右就能完成一次 快排。

    void quickSort2(int x[], int l, int r){
    	if(l >= r)
    		return;
    	int m = l;
    	for(int i = l + l; i <= r; i++ ){
    		if(x[i] < x[l]){
    			swap2(x[++m], x[i]);
    		}
    	}
    	swap2(x[l], x[m]);
    	quickSort2(x, l, m - 1);
    	quickSort2(x, m + 1, r);
    
    }
    void swap2(int &a,int &b){
    	if(a==b) return;//对同一地址的数据交换,会使其结果为0
    	a=a^b;
    	b=a^b;
    	a=a^b;
    }
    代码是不是更简单了?程序先进行判断,如果l>=r直接return,这点跟双边扫描的if一个意思,都是为递归创造结束的标志。然后用m记录最左边的那个的索引,这里默认的是第一个,即x[l]的索引。[注,m的初始值不一定指向key!,仅仅是指向最左边的。]然后进入扫描,直接从l + 1开始,如果右边的小于key,就让x[++m]和x[i]交换。如果右边的大于key,则不进行任何操作。这里有个特例,如果l = 0, 则m = 0.如果x[1]小于x[0],则让x[1] 和x[1]进行交换,也就等于没交换。如果数组是5 4 3 2 1,则这里的交换就失效了。 再往后看,直到for循环结束,走出循环,让最后m指的位置的数和最初的key进行交换。如上面 5 4 3 2 1,则第一次快排的结果是 1 4 3 2 5,只有for出来后的那次swap才起作用。这里的m有个特殊含义,即指向小于key的最右边的那个数。所以出来后才用它(x[m])和key(即x[l])进行交换。

    单边扫描的特点:

    1、程序需要交换;

    2、更有冒泡法的色彩;冒泡的目的不是让最大的数沉到最右边,而是让小于key的都左移,找到分界索引m。使之和key进行交换。

    3、此版本的的单边扫描属于最基础的,还可以优化。

    本想测出两个算法的时间 消耗差异,遗憾的是c++获得程序运行时间太费劲了,弄半天没弄成。下面附上完整程序:

    //============================================================================
    // Name        : QuikSort.cpp
    // Author      : YanZi
    // Version     :
    // Copyright   : Your copyright notice
    // Description : Hello World in C++, Ansi-style
    //============================================================================
    
    #include <iostream>
    #include <malloc.h>
    
    using namespace std;
    void swap1(int a, int b);
    void printArray(int* in, int n);
    
    void quickSort1(int* x, int l, int r);//双边扫描,快速排序
    void quickSort2(int x[], int l, int r);//单边扫描,快速排序
    void swap2(int &a,int &b); //交换,在MinGW上必须采用此方法,swap1无效
    
    
    #define N 8 //数组的长度
    
    int main() {
    	int* input = NULL;
    	input = (int*)malloc(N * sizeof(int));
    	if(input == NULL){
    		cout<<"内存溢出"<<endl;
    	}
    	for(int i = 0; i < N; i++){
    		input[i] = rand();
    	}
    	//	int input[] = {55, 41, 59, 26, 53, 58, 97, 93};
    
    	cout<<"原始数据:"<<endl;
    	printArray(input, N);
    
    	quickSort2(input, 0, N-1);
    	printArray(input, N);
    
    	return 0;
    }
    void swap1(int a, int b){
    	int temp = a;
    	a = b;
    	b = temp;
    }
    void printArray(int * in, int n){
    	if(in == NULL){
    		return;
    	}
    	for(int i = 0; i<n; i++){
    		cout<<" "<<in[i];
    	}
    	cout<<endl;
    
    }
    
    void quickSort1(int* x, int l, int r){
    
    	if(l < r){
    		int i = l, j = r, key = x[l];
    		while(i < j){
    			while( i < j && x[j] >= key){
    				j--;
    			}
    			if(i < j){
    				x[i++] = x[j];
    			}
    			while(i < j && x[i] <= key){
    				i++;
    			}
    			if(i < j){
    				x[j--] = x[i];
    			}
    		}
    		cout<<"i = " <<i<<" j = "<<j<<endl;
    		x[i] = key;
    		quickSort1(x, l, i-1);
    		quickSort1(x, i+1, r);
    	}
    
    }
    void quickSort2(int x[], int l, int r){
    	if(l >= r)
    		return;
    	int m = l;
    	for(int i = l + l; i <= r; i++ ){
    		if(x[i] < x[l]){
    			swap2(x[++m], x[i]);
    		}
    	}
    	swap2(x[l], x[m]);
    	quickSort2(x, l, m - 1);
    	quickSort2(x, m + 1, r);
    
    }
    void swap2(int &a,int &b){
    	if(a==b) return;//对同一地址的数据交换,会使其结果为0
    	a=a^b;
    	b=a^b;
    	a=a^b;
    }




    展开全文
  • 加权平均

    千次阅读 2018-07-09 13:39:04
    在日常生活中,我们经常提到“平均数”。一般我们在求“平均数”时,通常是用“一组数据中所有数据之和再除以数据的个数”。但是,这种叫法是不准确的。 一般来说,“平均数”大致可以分为7类。即:“算数平均数”...
  • 其实, 这涉及带宽的两种含义, wikipedia中说得很清楚了。 1. 在通信中, 带宽的单位是Hz, 学过"信号与系统"的人都知道。 2. 在计算中, 带宽等价于比特率(bps), 其实叫比特率更合适。 很多人装了1M...
  • 仔细分析这两种数据排名,我们发现:中国在 AI 方向确实已经处于前沿地位,但我们在将 AI 应用于传统基础学科上仍有欠缺。这种欠缺,除了提示我们应当更加注重基础研究外,或许也告诉我们的 AI 专家们:在传统学科...
  • 箱形图含义

    万次阅读 多人点赞 2018-05-06 16:59:48
    具体的计算目前有(n+1)/4与(n-1)/4两种,一般使用(n+1)/4。 举个例子,有有序序列一个test = c(1,2,3,4,5,6,7,8),通过summary(test)来获取test这个序列的中位数,上四分位数,下四分位数以及算数平均值。 这个Q1=...
  • 1.Dice系数 Dice距离主要是用来计算个集合的相似性的(也可以度量字符串的相似性)....在已知精确率和召回率的情况下 求得的一种平均的结果. 3. 各种指标的含义 precision: 预测为对的当中,原本是对的比...
  • 论文中AP与AR含义详细解释

    千次阅读 2019-12-02 14:49:31
    这里的AP通过和IOU结合定义出两种分别: 1.当IOU大于0.5认定为真 2.当IOU大于0.75认定为真 3.从IOU大于0.5开始,每次增加0.05,分别认定为真,然后平均求得AP值 COCO数据集平均每张图片包含 3.5个类别和 7.7 个实例...
  • 本文介绍了计算“完成时间、周转时间、平均周转时间、带权周转时间和平均带权周转时间”的公式,并且用先来先服务(FCFS)、短作业优先(SJF)两种调度算法来分析一个例题。
  • 前三种平均数是根据总体所有标志值计算的所以称为数值平均数,后两种平均数是根据标志值所处的位置确定的,因此称为位置平均数。   1、算术平均数的计算  算术平均数是计算平均指标的最常用方法,它的基本...
  • 硬盘转速和平均寻道时间

    万次阅读 2017-05-08 12:45:26
    设一个磁盘的平均寻道时间为20ms...答案:读写一个512字节的扇区的平均时间为28.1ms平均旋转延时 = 0.5/5400转/ = 0.0056秒 = 5.6ms平均磁盘访问时间 = 平均寻道时间 + 平均旋转延时 + 传输时间 + 控制器延时 = 20ms
  • 一文带你了解Linux平均负载之谜

    千次阅读 2017-08-16 10:37:43
    译者注:作者从介绍linux平均负载演变历史及三个关键数值开始,对比不间断任务状态的过去与现在以及如何检测,分解平均负载指标,结合其他指标,分析平均负载优劣这几个方面,多维度解释linux平均负载的含义平均...
  • 卷积的物理含义

    千次阅读 2015-12-26 16:08:17
    卷积的物理含义与其在概率论中的一些应用
  • flink ui含义图解

    千次阅读 2020-09-02 19:47:23
    笔者最近开始学习flink,但是flink的webui上各种指标错综复杂,在网上也没有找到一个比较详尽的资料,于是个人整理了一下关于flink中taskmanager的webui各个指标的含义,供大家参考。 注:括号中仅为个人理解 如下...
  • 本来应该分成两篇来写的,但是这两种中心的算法和应用都很接近,所以就合并成一篇文章来写了。   昨天讲了中心要素,因为中心要素是要从原来的要素中去选择一个已有的,所以算出来的,与我们观念和感知中的“中心...
  • 数据库库分表策略的具体实现方案

    万次阅读 多人点赞 2017-01-02 14:10:03
    库分表的策略相对于前边两种复杂一些,一种常见的路由策略如下: 1、中间变量 = user_id%(库数量*每个库的表数量); 2、库序号 = 取整(中间变量/每个库的表数量); 3、表序号 = 中间变量%每个库...
  • 寄售的含义

    千次阅读 2009-03-21 11:17:00
    寄售的含义1、采购的寄售物料是指供应商把东西送到公司,公司在生产耗用后才产生会计凭证.一般适用于大量且比较稳定的生产原料.2、从SD的角度来说-----客户从公司下了订单,将物料提走,但并未付款,此时对于公司来说...
  • 与mAP相关的还有平均精度(AP)、平均精度均值(mAP)、查准率(precision)、查全率(recall)、IOU、置信度阈值(confidence thresholds)等概念。 查准率(precision)和查全率(recall)。对于二分类问题,可将...
  • T检验和p-value含义及计算公式

    万次阅读 多人点赞 2018-01-30 14:50:32
    T检验是用于小样本(样本容量小于30)的平均值差异程度的检验方法。它是用T分布理论来推断差异发生的概率,从而判定平均数的差异是否显著。  目的:比较样本均数 所代表的未知总体均数μ和已知...
  • 目录 个人关于Houdini的一些看法 快捷键 视图窗口 ...同时经验与思路非常重要,就算死记硬背了各个节点的含义,也没法很好的驾驭它,就像弹钢琴知道每个键的音和位置,脑子里还得有乐谱才能弹奏好...
  • http和www含义讲解

    万次阅读 2019-02-12 10:41:54
    其实www和http://完全是回事啦,www是二级域名,而http则是一传输协议,实际上当我们在浏览器内输入www.baidu.com的时候,浏览器会自动帮你填充http://,不信的话你可以尝试输入www.csdn.net,然后将地址栏内容...
  • load average的详细含义

    千次阅读 2016-04-27 00:09:09
    系统平均负载被定义为在特定时间间隔内运行队列中的平均进程数。如果一个进程满足以下条件则其就会位于运行队列中: 它没有在等待I/O操作的结果 它没有主动进入等待状态(也就是没有调用'wait') 没有被停止(例如:...
  • 使用给定的预训练单词嵌入,可以通过计算“一个文档的嵌入单词需要“移动”以到达另一文档的嵌入单词所需的最小距离”来用语义含义来度量文档之间的差异。 在以下各节中,我们将讨论WMD的原理,WMD的约束和近似,...
  • 深度学习中mAP的含义

    千次阅读 2019-07-10 09:04:10
    一、mAP相关概念 首先要给大家介绍几个...现在假设我们的分类目标只有类,计为正例(positive)和负例(negtive),然后我们就能得到如下的四情况: (1)True positives(TP):被正确地划分为正例的个数,即...
  • 一、为什么库分表 我们知道每台机器无论配置多么好它都有自身的物理上限,所以当我们应用已经能触及或远远超出单台机器的某个上限的时候,我们惟有寻找别的机器的帮助或者继续升级的我们的硬件,但常见的方案还是...
  • KL散度的含义与性质

    万次阅读 多人点赞 2018-06-09 14:35:36
    在概率论或信息论中,KL散度( Kullback–Leibler divergence),又称相对熵(relative entropy),是描述个概率分布P和Q差异的一方法。它是非对称的,这意味着D(P||Q) ≠ D(Q||P)。特别的,在信息论中,D(P||Q)...
  • [机器学习]二k-means算法详解

    万次阅读 2017-02-05 21:00:05
    分层聚类的策略一般有两种: 聚合。这是一种自底向上的方法,每一个观察者初始化本身为一类,然后两两结合 分裂。这是一种自顶向下的方法,所有观察者初始化为一类,然后递归地分裂它们   二k-means算法是分裂法...
  • 趋势分析之移动平均线

    千次阅读 2011-08-17 08:09:55
    趋势分析之移动平均线  趋势分析原理是外汇交易技术分析的重要内容。外汇市场的价格走势总是呈现趋势运动,因此,了解和把握外汇交易的趋势分析原理,对于准确地分析和预测外汇价格的走势具有重要意义。  一...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 77,008
精华内容 30,803
关键字:

平均分的两种含义