Home | Administração | Fórum | Livros | WWW | Diário | Tarefas | Alunos |
Esta página foi extraída (com várias modificações) do capítulo 10 (Linear Structures) do livro de Eric Roberts.
Uma fila (= queue) é uma estrutura linear sujeita às operações de inserção e remoção. Essas operações obedecem à política FIFO (= first in, first out): o objeto removido é sempre o que foi inserido há mais tempo.
Diz-se que uma das extremidades de uma fila é o início ou cabeça (= head) e a outra extremidade é o fim ou cauda (= tail) da fila. Objetos são inseridos no fim da fila e são removidos do início da fila.
Segue uma implementação de uma fila de objetos do tipo elementT. A fila será implementada em uma lista encadeada. As células da lista serão do tipo cellT.
typedef struct cellT { elementT *element; struct cellT *link; } cellT; ////////////////////////////////////////////////////////// // Implementação de uma fila (= queue) // A fila será representada por um registro do tipo queueT // que tem dois campos: o endereço do início da fila e o // endereço do fim da fila. Uma fila q será considerada // vazia se q->head == NULL. A fila propriamente dita será // uma lista encadeada com células do tipo cellT. ////////////////////////////////////////////////////////// typedef struct { cellT *head; cellT *tail; } queueT; // Operação de inserção: coloca element na fila q. void Enqueue( queueT *q, elementT *element) { cellT *cp; cp = malloc( sizeof (cellT)); if (cp == NULL) exit( EXIT_FAILURE); cp->element = element; cp->link = NULL; if (q->head == NULL) q->head = cp; else q->tail->link= cp; q->tail = cp; } // Operação de remoção: retira um objeto da fila q e // devolve o valor desse objeto. elementT *Dequeue( queueT *q) { elementT *result; cellT *cp; cp = q->head; if (cp == NULL) { printf( "Erro: a fila está vazia"); exit( EXIT_FAILURE); } result = cp->element; q->head = cp->link; free( cp); return result; }
Além das funções de inserção e remoção, é bom que o pacote de manipulação de filas tenha algumas funções "auxiliares":
// Cria uma fila vazia e devolve o endereço da nova fila. queueT *NewQueue( void) { queueT *q; q = malloc( sizeof (queueT)); if (q == NULL) exit( EXIT_FAILURE); q->head = NULL; return q; } // Libera o espaço ocupado pela fila q. A função supõe que // o espaço apontado pelo campo element de cada célula da // lista tenha sido alocado por malloc. void FreeQueue( queueT *q) { cellT *cp, *next; cp = q->head; while (cp != NULL) { next = cp->link; free( cp->element); free( cp); cp = next; } free( q); } // Devolve 1 se a fila q está vazia e 0 em caso contrário. int QueueIsEmpty( queueT *q) { return q->head == NULL; } // Devolve o comprimento da fila q. int QueueLength( queueT *q) { int n; cellT *cp; n = 0; for (cp = q->head; cp != NULL; cp = cp->link) n++; return n; }
Vamos usar a implementação de fila descrita acima, apenas trocando elementT por clienteT e portanto a definição de cellT por
typedef struct cellT { clienteT *element; struct cellT *link; } cellT;
Observe que o programa de simulação faz questão de manipular a fila apenas através das funções Enqueue, Dequeue, NewQueue, FreeQueue, QueueIsEmpty e QueueLength. O programa de simulação faz questão de não se referir diretamente as campos head, tail, link da estrutura da fila.
// Programa checkout.c // ------------------- // Este programa simula a fila diante de um caixa de supermercado. // Clientes chegam e entram na fila. Cada cliente espera na fila // até que o caixa esteja livre. Quando isso acontece, o caixa // tira o primeiro cliente da fila e passa a atendê-lo durante um // certo tempo, escolhido aleatoriamente entre os parâmetros // TempoMinAtendimento e TempoMaxAtendimento. Quando tiver // terminado o atendimento, o caixa dispensa o cliente e chama o // próximo da fila. // // Em cada unidade de tempo, começando na unidade 0 e terminando // na unidade DuracaoDaSimulacao, o programa executa as seguintes // operações: // 1. Verifica se um novo cliente chegou. Novos clientes chegam // aleatoriamente com probabilidade determinada pelo parâmetro // ProbabilidadeDeEntrada. // 2. Se o tempo de atendimento do cliente ativo (ou seja, do // cliente que está sendo atendido) já estiver esgotado, o // cliente ativo é dispensado. Senão, uma unidade é subtraída // do tempo de atendimento do cliente ativo. // 3. Se o caixa estiver livre, o programa chama o próximo cliente // da fila. #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> typedef enum {FALSE, TRUE} bool; ////////////////////////////////////////////////////////////// // Seção 1: Parametros da simulação e o tipo-de-dados clienteT ////////////////////////////////////////////////////////////// // DuracaoDaSimulacao: quantas unidades de tempo vai durar a // simulação // ProbabilidadeDeEntrada: probabilidade de que um novo cliente // entre na fila durante a unidade de tempo // TempoMinAtendimento: duração mínima de permanência do cliente // no caixa // TempoMaxAtendimento: duração máxima de permanência do cliente // no caixa #define DuracaoDaSimulacao 200 #define ProbabilidadeDeEntrada 0.2 #define TempoMinAtendimento 5 #define TempoMaxAtendimento 20 // Tipo clienteT: Cada cliente é um struct que contém // . número do cliente // . instante de entrada na fila // . tempo que o cliente ficará no caixa (um número aleatório // entre TempoMinAtendimento and TempoMaxAtendimento) typedef struct { int numeroCliente; int instanteEntrada; int tempoDeAtendimento; } clienteT; //////////////////////////////////////////////////////////// // Seção 3: Demais tipos-de-dados da simulação e protótipos // de funções //////////////////////////////////////////////////////////// // Tipo dadosDaSimulacaoT: Esse tipo-de-dados armazena os dados // da simulação. Eis alguns dos campos: // . clienteAtivo: vale NULL ou aponta para o cliente que está // sendo atendido // . fila: aponta para a fila de clients (o cliente ativo não // está mais na fila) // . somaComprimentos: é a soma dos comprimentos da fila em // todos os instantes da simulação typedef struct { queueT *fila; clienteT *clienteAtivo; int tempo; int numeroDeClientes; int numeroDeAtendidos; int somaTemposDeEspera; int somaComprimentos; } dadosDaSimulacaoT; ////////////////////////////////////////////////// // Seção 4: Programa principal // --------------------------- // Digite "-t" se deseja que o programa imprima um // histórico da simulação. ////////////////////////////////////////////////// void ExecutaSimulacao( dadosDaSimulacaoT *sim); void ColocaClienteNaFila( dadosDaSimulacaoT *sim); void ProcessaFila( dadosDaSimulacaoT *sim); void AtendeCliente( dadosDaSimulacaoT *sim); void DispensaCliente( dadosDaSimulacaoT *sim); void Randomize( void); int RandomInteger( int low, int high); double RandomReal( double low, double high); bool RandomChance( double p); bool traceFlag; // se TRUE, programa imprime histórico da simulação int main( int argc, char *argv[]) { dadosDaSimulacaoT dados; if (argc >= 2 && strcmp( argv[1], "-t") == 0) traceFlag = TRUE; else traceFlag = FALSE; Randomize(); ExecutaSimulacao( &dados); printf( "\n"); printf( "Resultados da simulação, dados os seguintes parâmetros:\n"); printf( " DuracaoDaSimulacao: %4d\n", (int) DuracaoDaSimulacao); printf( " ProbabilidadeDeEntrada: %7.2f\n", (double) ProbabilidadeDeEntrada); printf( " TempoMinAtendimento: %4d\n", (int) TempoMinAtendimento); printf( " TempoMaxAtendimento: %4d\n", (int) TempoMaxAtendimento); printf( "\n"); printf( "Clientes atendidos: %4d\n", dados.numeroDeAtendidos); printf( "Tempo médio de espera: %7.2f\n", (double) dados.somaTemposDeEspera / dados.numeroDeAtendidos); printf( "Comprimento médio da fila: %7.2f\n", (double) dados.somaComprimentos / DuracaoDaSimulacao); return 0; } // Esta função recebe o endereço sim do struct que contém os // dados da simulação e faz a simulação. void ExecutaSimulacao( dadosDaSimulacaoT *sim) { sim->fila = NewQueue(); sim->clienteAtivo = NULL; sim->numeroDeClientes = 0; // Roberts esqueceu? sim->numeroDeAtendidos = 0; sim->somaTemposDeEspera = 0; sim->somaComprimentos = 0; for (sim->tempo = 0; sim->tempo < DuracaoDaSimulacao; sim->tempo++) { if (RandomChance( ProbabilidadeDeEntrada)) ColocaClienteNaFila( sim); ProcessaFila( sim); } FreeQueue( sim->fila); } // Esta função simula a chegada de um novo cliente. void ColocaClienteNaFila( dadosDaSimulacaoT *sim) { clienteT *c; sim->numeroDeClientes++; c = malloc( sizeof (clienteT)); c->numeroCliente = sim->numeroDeClientes; c->instanteEntrada = sim->tempo; c->tempoDeAtendimento = RandomInteger( TempoMinAtendimento, TempoMaxAtendimento); Enqueue( sim->fila, c); if (traceFlag) printf( "%4d: Cliente %d entra na fila\n", sim->tempo, sim->numeroDeClientes); } // Esta função processa os eventos em uma unidade de tempo // da simulação da fila. void ProcessaFila( dadosDaSimulacaoT *sim) { if (sim->clienteAtivo == NULL) { if (!QueueIsEmpty( sim->fila)) AtendeCliente( sim); } else { if (sim->clienteAtivo->tempoDeAtendimento == 0) DispensaCliente( sim); else sim->clienteAtivo->tempoDeAtendimento--; } sim->somaComprimentos += QueueLength( sim->fila); } // Esta função é invocada quando o caixa está livre e há um // cliente esperando para ser atendido. O caixa tira o primeiro // cliente da fila e começa a atendê-lo. void AtendeCliente( dadosDaSimulacaoT *sim) { clienteT *cli; cli = Dequeue( sim->fila); sim->clienteAtivo = cli; sim->numeroDeAtendidos++; sim->somaTemposDeEspera += (sim->tempo - cli->instanteEntrada); if (traceFlag) printf( "%4d: Cliente %d chega ao caixa\n", sim->tempo, cli->numeroCliente); } // Esta função é invocada quando a contagem regressiva do // atendimento do cliente ativo atinge zero. O cliente vai embora // e o caixa fica livre. void DispensaCliente( dadosDaSimulacaoT *sim) { if (traceFlag) printf( "%4d: Cliente %d deixa o caixa\n", sim->tempo, sim->clienteAtivo->numeroCliente); free( sim->clienteAtivo); sim->clienteAtivo = NULL; } ///////////////////////////////////////////////// // Seção 5: Gerador de números (pseudo)aleatórios ///////////////////////////////////////////////// // A função Randomize inicializa o gerador de números aletórios // de modo que os resultados das chamadas a RandomInteger e // RandomReal sejam imprevisíveis. Se essa função não for chamada, // as outras funções produzirão sempre a mesma seqüência de // valores. void Randomize( void) { srand( (int) time( NULL)); } // A função RandomReal devolve um inteiro aleatório no intervalo // fechado [low..high]. double RandomReal( double low, double high) { double d; d = (double) rand() / ((double) RAND_MAX + 1); return low + d * (high - low); } // A função RandomInteger devolve um número real aleatório no // intervalo semi-aberto [low..high). int RandomInteger( int low, int high) { int k; double d; d = (double) rand() / ((double) RAND_MAX + 1); k = d * (high - low + 1); return low + k; } // A função RandomChance recebe um número real p no intervalo // fechado [0..1] e devolve TRUE com probabilidade p e FALSE com // probabilidade 1 - p. bool RandomChance( double p) { return RandomReal( 0, 1) < p; }
Resultados de um teste:
5: Cliente 1 entra na fila 5: Cliente 1 chega ao caixa 16: Cliente 1 deixa o caixa 17: Cliente 2 entra na fila 17: Cliente 2 chega ao caixa 18: Cliente 3 entra na fila 19: Cliente 4 entra na fila 20: Cliente 5 entra na fila 21: Cliente 6 entra na fila 32: Cliente 2 deixa o caixa . . . 180: Cliente 13 deixa o caixa 181: Cliente 14 chega ao caixa 185: Cliente 34 entra na fila 191: Cliente 14 deixa o caixa 192: Cliente 15 chega ao caixa 193: Cliente 35 entra na fila Resultados da simulação, dados os seguintes parâmetros: DuracaoDaSimulacao: 200 ProbabilidadeDeEntrada: 0.20 TempoMinAtendimento: 5 TempoMaxAtendimento: 20 Clientes atendidos: 15 Tempo médio de espera: 50.60 Comprimento médio da fila: 9.53