1. 程式人生 > >離散事件模型

離散事件模型

模擬 nod unsigned del 指針 內容 lis 數組a 修改

0x01 代碼框架邏輯

模擬內容:

  1.離散事件模擬,模擬銀行營業時的排隊情況
  2.不考慮顧客中途離開,顧客到達事件隨機,業務辦理時間
  3.長度隨機,選擇最短的隊排隊,不再換隊

代碼邏輯:

  1.一個事件鏈表,四個窗口排隊隊列

  2.事件驅動:每有一個新的顧客到達,將產生下一個新顧客到達的新事件按時間順序從小到大(OccurTime)插入事件鏈表(EventList) (如果此時窗口隊列只有 一個顧客,還將產生此顧客離開事件插入事件鏈表中)
     每有一個顧客從某一隊列首離開,將產生他的後一位顧客離開的新事件按時間順序從小到大(OccurTime)插入事件鏈表(EventList)

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef int Status;
typedef struct Event {   //事件類型
	int OccurTime;  //事件發生時刻
	int NType;      //事件類型,0表示到達事件,1至4表示四個窗口的離開事件
	struct Event *next;
}Event, ElemType;

typedef struct { //單向鏈表結構
	ElemType *head;//頭指針
	ElemType *tail;//尾指針
	int len;    //長度
}LinkList;

typedef LinkList EventList; //事件鏈表

typedef struct QElemType { //隊列元素
	int ArriveTime;//到達時間
	int Duration;//辦理業務所需時間
	struct QElemType *next;
}QElemType;

typedef struct {//隊列結構
	QElemType *head;//頭指針
	QElemType *tail;//尾指針
}LinkQueue;

Event NewEvent(int occurT, int nType);
//根據OccurTime和NType值,創建新事件
Status InitList(LinkList *L);
//初始化事件鏈表
Status OrderInsert(LinkList *L, Event e);
//將事件e按發生時間順序插入有序鏈表L中
Status ListEmpty(LinkList *L);
//判斷鏈表L是否為空,為空返回TRUE,否則返回FALSE
Status DelFirst(LinkList *L, ElemType *e);
//鏈表L不為空,刪除其首結點,用e返回,並返回OK;否則返回ERROR
Status ListTraverse(LinkList *L);
//遍歷鏈表
Status InitQueue(LinkQueue *Q);
//初始化隊列Q
Status EmptyQueue(LinkQueue *Q);
//若隊列Q為空,返回TRUE,否則返回FALSE
Status DelQueue(LinkQueue *Q, QElemType *e);
//若隊列Q不為空,首結點出隊,用e返回,並返回OK;否則返回ERROR
Status EnQueue(LinkQueue *Q, QElemType e);
//結點e入隊Q
int QueueLength(LinkQueue Q);
//返回隊列Q的長度,即元素個數
Status GetHead(LinkQueue *Q, QElemType *e);
//若隊列Q不為空,用e返回其首結點,並返回OK,否則返回ERROR
Status QueueTraverse(LinkQueue *Q);
//遍歷隊列Q

//------------------//
int Min(int a[], int n);
//返回長度為n的數組a第一個最小值的下標,從1開始
int ShortestQueue();
//獲取最短隊列的編號
void OpenForDay();
//初始化操作
void CustomerArrived();
//顧客達到事件
void CustomerDepature();
//顧客離開事件
void Bank_Simulation();
//銀行排隊模擬
void PrintEventList();
//輸出事件隊列
void PrintQueue();
//打印當前隊列
//----全局變量-----//
EventList ev;
Event en;
LinkQueue q[5];
QElemType customer;
int TotalTime, CustomerNum;
int CloseTime = 50;//關閉時間,即營業時間長度

				   //--------------main()------------------//
int main()
{
	Bank_Simulation();
	return 0;
}


//--------------模擬排隊----------------//
void OpenForDay() {
	//初始化操作
	int i;
	TotalTime = 0;    CustomerNum = 0;
	InitList(&ev);//初始化事件鏈表
	en.OccurTime = 0;
	en.NType = 0;
	OrderInsert(&ev, en);
	for (i = 1; i <= 4; i++)
		InitQueue(&q[i]);//初始化四個窗口隊列
}//OpenForDay

void CustomerArrived() {
	//顧客達到事件
	int durtime, intertime, i, t;
	QElemType e;
	++CustomerNum;
	intertime = rand() % 5 + 1;//間隔時間在5分鐘內
	durtime = rand() % 30 + 1;//辦理業務時間在30分鐘內
	t = en.OccurTime + intertime;
	if (t<CloseTime) {//銀行尚未關門
		printf("A new customer will arrive at:%d\n", en.OccurTime);//下一位顧客達到時間
		OrderInsert(&ev, NewEvent(t, 0));
		i = ShortestQueue();//最短隊列
		e.ArriveTime = en.OccurTime;
		e.Duration = durtime;
		EnQueue(&q[i], e);
		if (QueueLength(q[i]) == 1)
			OrderInsert(&ev, NewEvent(en.OccurTime + durtime, i));
	}
}

void CustomerDepature() {
	//顧客離開事件
	int i = en.NType;
	DelQueue(&q[i], &customer);
	printf("A customer leaves at:%d\n", en.OccurTime);//輸出顧客離開時間
	TotalTime += en.OccurTime - customer.ArriveTime;
	if (!EmptyQueue(&q[i])) {
		GetHead(&q[i], &customer);
		OrderInsert(&ev, NewEvent(en.OccurTime + customer.Duration, i));
	}
}

void Bank_Simulation() {
	//銀行排隊模擬
	OpenForDay();
	srand((unsigned)time(NULL));
	while (!ListEmpty(&ev)) {
		DelFirst(&ev, &en);  //事件驅動
		if (en.NType == 0)
			CustomerArrived();
		else
			CustomerDepature();
		//PrintEventList();
		PrintQueue();
	}
	printf("\nTotal time is: %d min,average time is: %g min.\n", TotalTime, (float)TotalTime / CustomerNum);
}

void PrintQueue() {
	//打印當前隊列
	int i;
	for (i = 1; i <= 4; i++) {
		printf("Queue %d have %d customer(s):", i, QueueLength(q[i]));
		QueueTraverse(&q[i]);
	}
	printf("\n");
}

void PrintEventList() {
	//輸出事件隊列
	printf("Current Eventlist is:\n");
	ListTraverse(&ev);
}
int Min(int a[], int n) {
	//返回長度為n的數組a第一個最小值的下標,從0開始
	int i, tmp, ind = 0;
	tmp = a[0];
	for (i = 1; i<n; i++) {
		if (a[i]<tmp) {
			tmp = a[i];
			ind = i;
		}
	}
	return ind;
}

int ShortestQueue() {
	//獲取最短隊列的編號
	int i, a[4];
	for (i = 1; i <= 4; i++) {
		a[i - 1] = QueueLength(q[i]);
		//printf("隊%d的長度為%d\n",i,QueueLength(q[i]));
	}
	return Min(a, 4) + 1;//隊列從1開始編號
}

//-----------隊和鏈表操作--------------//
Event NewEvent(int occurT, int nType) {
	//根據OccurTime和NType值,創建新事件
	Event e;
	e.OccurTime = occurT;
	e.NType = nType;
	return e;
}

Status InitList(LinkList *L) {
	//初始化事件鏈表
	L->head = L->tail = (ElemType *)malloc(sizeof(ElemType));
	if (!L->head) {
		printf("Apply for memory error.LinkList initialize failed.\n");
		exit(0);
	}
	L->head->next = NULL;
	return OK;
}

Status OrderInsert(LinkList *L, Event e) {
	//將事件e按發生時間順序插入有序鏈表L中
	ElemType *p, *q, *newptr;
	newptr = (ElemType *)malloc(sizeof(ElemType));
	if (!newptr) {
		printf("Apply for memory error,new node can‘t insert intot the Eventlist.\n");
		exit(0);
	}
	*newptr = e;
	if (TRUE == ListEmpty(L)) {//鏈表為空
		L->head->next = newptr;
		L->tail = newptr;
		L->tail->next = NULL;
		return OK;
	}
	q = L->head;
	p = L->head->next;
	while (p) {//遍歷整個鏈表
		if (p->OccurTime >= newptr->OccurTime)
			break;
		q = p;
		p = p->next;
	}
	q->next = newptr;
	newptr->next = p;
	if (!p)//插入位置為鏈表尾部
		L->tail = newptr;
	return OK;
}

Status ListEmpty(LinkList *L) {
	//判斷鏈表L是否為空,為空返回TRUE,否則返回FALSE
	if ((L->head == L->tail) && (L->head != NULL))
		return TRUE;
	else
		return FALSE;
}

Status DelFirst(LinkList *L, ElemType *e) {
	//鏈表L不為空,刪除其首結點,用e返回,並返回OK;否則返回ERROR
	ElemType *p = L->head->next;
	if (!p)
		return ERROR;
	L->head->next = p->next;
	*e = *p;
	free(p);
	if (L->head->next == NULL)
		L->tail = L->head;
	return OK;
}

Status ListTraverse(LinkList *L) {
	//遍歷鏈表
	Event *p = L->head->next;
	if (!p) {
		printf("List is empty.\n");
		return ERROR;
	}
	while (p != NULL) {
		printf("OccurTime:%d,Event Type:%d\n", p->OccurTime, p->NType);
		p = p->next;
	}
	printf("\n");
	return OK;
}

Status InitQueue(LinkQueue *Q) {
	//初始化隊列Q
	Q->head = Q->tail = (QElemType *)malloc(sizeof(QElemType));
	if (!Q->head) {
		printf("Apply for memory error.LinkQueue initialize failed.\n");
		exit(0);
	}
	Q->head->next = NULL;
	return OK;
}

Status EmptyQueue(LinkQueue *Q) {
	//若隊列Q為空,返回TRUE,否則返回FALSE
	if (Q->head == Q->tail&&Q->head != NULL)
		return TRUE;
	else
		return FALSE;
}

Status DelQueue(LinkQueue *Q, QElemType *e) {
	//若隊列Q不為空,首結點出隊,用e返回,並返回OK;否則返回ERROR
	QElemType *p = Q->head->next;
	if (!p)
		return ERROR;
	*e = *p;
	Q->head->next = p->next;//修正隊首指針
	free(p);
	if (!Q->head->next)//隊空
		Q->tail = Q->head;
	return OK;
}

Status EnQueue(LinkQueue *Q, QElemType e) {
	//結點e入隊Q
	QElemType *p = (QElemType *)malloc(sizeof(QElemType));
	if (!p) {
		printf("Apply for memory error,new element can‘t enqueue.\n");
		exit(0);
	}
	*p = e;
	p->next = NULL;
	Q->tail->next = p;//插入隊尾
	Q->tail = p;//修改隊尾指針
	return OK;
}

int QueueLength(LinkQueue Q) {
	//返回隊列Q的長度,即元素個數
	int count = 0;
	QElemType *p = Q.head->next;
	while (p) {
		p = p->next;
		count++;
	}
	return count;
}

Status GetHead(LinkQueue *Q, QElemType *e) {
	//若隊列Q不為空,用e返回其首結點,並返回OK,否則返回ERROR
	if (EmptyQueue(Q))
		return ERROR;
	*e = *(Q->head->next);
	return OK;
}

Status QueueTraverse(LinkQueue *Q) {
	//遍歷隊列Q
	QElemType *p = Q->head->next;
	if (!p) {
		printf("--Is empty.\n");
		return ERROR;
	}
	while (p) {
		printf("(%d,%d) ", p->ArriveTime, p->Duration);
		p = p->next;
	}
	printf("\n");
	return OK;
}

  

離散事件模型