3.14. Simulação: Tarefas de Impressão¶
Uma simulação mais interessante nos permite estudar o comportamento de uma fila de impressão descrita anteriormente nesta seção. Lembre-se que como as alunas e os alunos enviam tarefas de impressão para uma impressora compartilhada, as tarefas são colocadas em uma fila para ser processada de maneira que a primeiro a chegar seja a primeiro a ser atendida. Muitas perguntas surgem com esta configuração. A mais importante delas pode ser se a impressora é capaz de lidar com uma certa quantidade de trabalhos. Se não for possível, as alunos e os alunos aguardarão muito tempo pela impressão e pode perder a próxima aula.
Considere a seguinte situação em um laboratório de ciência da computação. Em qualquer dia en média cerca de 10 alunas ou alunos estão trabalhando no laboratório em uma dado hora qualquer. Essas alunas e alunos costumam imprimir até duas vezes durante esse período e o tamamho dessas tarefas varia de 1 a 20 páginas. A impressora no laboratório é antiga, capaz de processar 10 páginas por minuto em qualidade de rascunho. A impressora pode ser trocada para oferecer melhor qualidade, mas produziria apenas cinco páginas por minuto. A velocidade de impressão mais lenta poderia fazer as alunas e os alunos esperarem demais. Qual frequência de impressão deve ser usada?
Poderíamos decidir em construir uma simulação que modela o laboratório. Nós precisará construir representações para as alunas e alunos, para astarefas de impressão e para a impressora (Figura 4). À medida que as alunas e alunos enviam tarefas de impressão, vamos adicioná-las a uma lista de espera, uma fila de tarefas de impressão associada a impressora. Quando a impressora concluir uma tarefa, ela examinará a fila para ver se há alguma tarefa restante a ser processada. De interesse para nós é a quantidade média de tempo que as alunas e alunos vão esperar para que seus trabalhos sejam impressos. Isso é igual à quantidade média de tempo que uma tarefa aguarda na fila.
Para modelar essa situação, precisamos usar algumas probabilidades. Por exemplo, as alunas e os alunos podem imprimir um documento de 1 a 20 páginas. Se cada comprimento de 1 a 20 é igualmente provável, a duração de uma tarefa de impressão pode ser simulada usando um número aleatório entre 1 e 20 inclusive. Isto significa que há chance igual de qualquer comprimento de impressão de 1 a 20 entrar na fila.
Se houver 10 alunas e alunos no laboratório e cada uma delas e deles imprimir duas vezes, haverá 20 tarefas de impressão por hora, em média. Qual é a probabilidade de que a qualquer dado segundo, uma tarefa de impressão será criada? A maneira de responder isso é considerar a razão de tarefas para o tempo. 20 tarefas por hora significa que, em média, haverá uma tarefa a cada 180 segundos:
\(\frac {20\ tarefas}{1\ hora} \times \frac {1\ hora} {60\ minutos} \times \frac {1\ minuto} {60\ segundos}=\frac {1\ task} {180\ segundos}\)
Para cada segundo podemos simular a chance de que uma tarefa de impressão ocorra gerando um número aleatório entre 1 e 180 inclusive. Se o número for 180, dizemos que uma tarefa foi criada. Note que é possível que muitas tarefas sejam criadas em seguida ou podemos esperar um bom tempo para uma nova tarefa aparecer. Essa é a natureza da simulação. Você quer simular o situação real, tanto quanto possível, dado que você sabe alguns parâmetros gerais.
3.14.1. Principais Passos da Simulação¶
Aqui está a simulação principal.
Crie uma fila de tarefas de impressão. Cada tarefa será associada ao instante que chegou na fila (timestamp). Inicialmente a fila está vazia.
Para cada segundo (
currentSecond
):- Uma nova tarefa de impressão é criada? Em caso afirmativo, adicione-a à fila com
o
currentSecond
como o instante associado.
Se a impressora não estiver ocupada e se uma tarefa estiver aguardando,
Remova a próxima tarefa da fila de impressão e atribua-a a impressora.
- Subtrai o instante associado a tarefa do
currentSecond
para computar o tempo de espera para essa tarefa.
- Subtrai o instante associado a tarefa do
- Anexe o tempo de espera dessa tarefa a uma lista para mais tarde ser
processada.
Com base no número de páginas da tarefa de impressão, calcule o tempo será necessário para imprimi-la.
- A impressora agora faz um segundo de impressão, se necessário. Também é
subtraido um segundo do tempo necessário para essa tarefa.
- Se a tarefa foi concluída, em outras palavras, o tempo necessário
chegou a zero, a impressora passa a estaar desocupada.
Após a conclusão da simulação, calcule o tempo médio de espera da lista de tempos de espera gerados.
3.14.2. Implementação em Python¶
Para projetar esta simulação, criaremos classes para os três
objetos do mundo real descritos acima: Printer
(Impressora), Task
(Tarefa) e
PrintQueue
(Fila de impressão).
A classe Printer
(Listagem 2) precisará rverificar se
há uma tarefa a ser impressa. Em caso afirmativo, ela está ocupado (linhas 13 a 17) e o
tempo necessário pode ser calculado a partir do número de páginas da tarefa.
O construtor também permitirá que a configuração de páginas por minuto seja
inicializada. O método tick
diminui o representa o cronômetro interno e
determina que a impressora fique inativa (linha 11) se a tarefa tiver sido concluída.
Listagem 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class Printer:
def __init__(self, ppm):
self.pagerate = ppm
self.currentTask = None
self.timeRemaining = 0
def tick(self):
if self.currentTask != None:
self.timeRemaining = self.timeRemaining - 1
if self.timeRemaining <= 0:
self.currentTask = None
def busy(self):
if self.currentTask != None:
return True
else:
return False
def startNext(self,newtask):
self.currentTask = newtask
self.timeRemaining = newtask.getPages() * 60/self.pagerate
|
A classe Task (Listagem 3) representará uma única tarefa de impressão.
Quando a tarefa é criada, um gerador de números aleatórios fornecerá um
comprimento de 1 a 20 páginas. Nós escolhemos usar para isso a função randrange()
do módulo random
.
>>> import random
>>> random.randrange(1,21)
18
>>> random.randrange(1,21)
8
>>>
Cada tarefa também precisará manter o instante em que foi criada (timestamp) a ser usado para
computar o tempo de espera. Esse registro de data e hora representará o tempo em que a tarefa foi
criada e colocada na fila da impressora. O método waitTime()
pode
então ser usado para recuperar a quantidade de tempo gasto na fila antes
a impressão começar.
Listagem 3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import random
class Task:
def __init__(self,time):
self.timestamp = time
self.pages = random.randrange(1,21)
def getStamp(self):
return self.timestamp
def getPages(self):
return self.pages
def waitTime(self, currenttime):
return currenttime - self.timestamp
|
A simulação principal (Listagem 4) implementa o algoritmo
descrito acima. O objeto printQueue
é uma instância da nossa TDA
fila (queue
) existente. Uma função auxiliar booleana, newPrintTask()
, decide
se uma nova tarefa de impressão foi criada. Nós decidimos usar novamente
a função randrange()
do módulo random
para retornar um
inteiro aleatório entre 1 e 180. As tarefas de impressão chegam uma vez a cada 180
segundos. Ao escolher arbitrariamente 180 do intervalo de inteiros aleatórios
(linha 28), podemos simular esse evento aleatório. A função de simulação
nos permite definir o tempo total e as páginas por minuto para o
impressora.
Listagem 4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | from pythonds.basic.queue import Queue
import random
def simulation(numSeconds, pagesPerMinute):
labprinter = Printer(pagesPerMinute)
printQueue = Queue()
waitingtimes = []
for currentSecond in range(numSeconds):
if newPrintTask():
task = Task(currentSecond)
printQueue.enqueue(task)
if (not labprinter.busy()) and (not printQueue.isEmpty()):
nexttask = printQueue.dequeue()
waitingtimes.append(nexttask.waitTime(currentSecond))
labprinter.startNext(nexttask)
labprinter.tick()
averageWait=sum(waitingtimes)/len(waitingtimes)
print("Average Wait %6.2f secs %3d tasks remaining."
%(averageWait,printQueue.size()))
def newPrintTask():
num = random.randrange(1,181)
if num == 180:
return True
else:
return False
for i in range(10):
simulation(3600,5)
|
Quando executamos a simulação, não devemos nos preocupar que o os resultados são diferentes a cada vez. Isto é devido à natureza probabilística dos números aleatórios. Estamos interessados nas tendências que podem estar ocorrendo como resultado dos ajustes dos parâmetros para a simulação. Aqui estão alguns resultados.
Primeiro, executaremos a simulação por um período de 60 minutos (3.600 segundos) usando uma taxa de imperssão de cinco páginas por minuto. Além disso, nós iremos executar 10 testes independentes. Lembre-se que como a simulação trabalha com números aleatórios cada execução retornará resultados diferentes.
>>> for i in range(10):
simulation(3600,5)
Average Wait 165.38 secs 2 tasks remaining.
Average Wait 95.07 secs 1 tasks remaining.
Average Wait 65.05 secs 2 tasks remaining.
Average Wait 99.74 secs 1 tasks remaining.
Average Wait 17.27 secs 0 tasks remaining.
Average Wait 239.61 secs 5 tasks remaining.
Average Wait 75.11 secs 1 tasks remaining.
Average Wait 48.33 secs 0 tasks remaining.
Average Wait 39.31 secs 3 tasks remaining.
Average Wait 376.05 secs 1 tasks remaining.
Depois de executar nossos 10 testes, podemos ver que o tempo médio de espera (Average Wait
)
é 122,09 segundos.
Você também pode ver que há uma grande variação no tempo médio de espera com um mínimo de 17,27
segundos e um máximo de 376,05 segundos.
Você também pode notar que em apenas dois dos casos foram todas as tarefas concluídas.
Agora, ajustaremos a taxa de páginas para 10 páginas por minuto e executaremos as 10 testes novamente, com uma frequência maior de páginas por segundo, nossa esperança seria que mais tarefas seria concluídas no período de uma hora.
>>>for i in range(10):
simulation(3600,10)
Average Wait 1.29 secs 0 tasks remaining.
Average Wait 7.00 secs 0 tasks remaining.
Average Wait 28.96 secs 1 tasks remaining.
Average Wait 13.55 secs 0 tasks remaining.
Average Wait 12.67 secs 0 tasks remaining.
Average Wait 6.46 secs 0 tasks remaining.
Average Wait 22.33 secs 0 tasks remaining.
Average Wait 12.39 secs 0 tasks remaining.
Average Wait 7.27 secs 0 tasks remaining.
Average Wait 18.17 secs 0 tasks remaining.
Você pode executar a simulação em ActiveCode 2.
3.14.3. Discussão¶
Nós estávamos tentando responder a uma pergunta sobre se a impressora poderia realmente lidar com a carga de tarefas se fosse configurado para imprimir com uma melhor qualidade e taxa de página mais lenta. A abordagem que tomamos foi escrever uma simulação que modelou as tarefas de impressão como eventos aleatórios de vários comprimentos e horários de chegada.
O resultado acima mostra que, com impressão de 5 páginas por minuto, o tempo médio de espera variou de uma de 17 segundos para uma alta de 376 segundos (cerca de 6 minutos). Com uma taxa de impressão mais rápida, a variação foi de 1 segundo com um máximo de apenas 28 segundos. Além disso, com 5 páginas por minuto, em 8 das 10 testes havia tarefas de impressão ainda esperando na fila ao final da hora.
Portanto, talvez tenhamos sido persuadidos que diminuir a velocidade da impressora para melhorar qualidade pode não ser uma boa ideia. Estudantes não podem se dar ao luxo de esperar por muito tempo por suas papeis, especialmente quando eles precisam ir para a próxima aula. Uma espera de seis minutos seria simplesmente longa demais.
Este tipo de análise de simulação nos permite responder a muitas perguntas, comumente conhecido como perguntas do tipo “se” (“what if”). Tudo o que precisamos fazer é variar os parâmetros utilizados pela simulação e podemos simular qualquer número de comportamentos interessantes. Por exemplo,
E se o número médio de alunas e alunos aumenta em 20?
E se for sábado e os estudantes não precisarem ir para a aula? Eles podem se dar ao luxo de esperar?
E se o tamanho da tarefa de impressão média diminuir desde que o Python é uma linguagem e programas tão poderosos tendem a ser muito mais curtos?
Todas estas questões podem ser respondidas modificando a simulação acima. No entanto, é importante lembrar que a simulação é tão boa quanto são as suposições usadas para construí-lo. Dados reais sobre o número de tarefas de impressão por hora e o número de alunas e alunos por hora era necessário construir uma simulação robusta.
Teste o seu conhecimento
Como você modificaria a simulação da impressora para refletir um número maior de estudantes? Suponha que o número de estudantes tenha dobrado. Você precisa fazer algumas suposições razoáveis sobre como essa simulação foi montada, mas o que você mudaria? Modifique o código. Suponha também que a duração da tarefa de impressão média tenha sido reduzida pela metade. Altere o código para refletir essa alteração. Finalmente, como você poderia parametrizar o número de alunos, em vez de alterar o código que gostaríamos? Gostaríamos de tornar o número de estudantes um parâmetro da simulação.