Saturday, March 24, 2012

Cálculo do tempo de processamento de algoritmos


Como já sabido, toda operação de computador requer um certo tempo e “esforço” do processador para acontecer. Quando programamos um computador, também é sabido que podemos produzir diversos códigos diferentes para obter o mesmo resultado, de forma similar a chegar a um mesmo resultado em Matemática a partir de distintas formas de fazer a mesma coisa. Contudo, meios diferentes requerem diferentes esforços, sejam nossos ou dos processadores, dando-nos mais ou menos trabalho, exigindo-nos mais ou menos tempo.

Com computadores, estamos falando de algoritmos, estas seqüências lógicas de processamento, se corretamente escolhidas, podem facilitar todo o processo na questão tempo e esforço.

O possível estimar o tempo de execução de algoritmos com a finalidade de saber quais são os melhores quando comparados uns com os outros, e é disso que venho falar.

A performance de um computador é determinada por fatores como:

·          Hardware:
o    Processador usado
o    Memória disponível
o    Disco disponível
·          Software
o    Linguagem e plataforma utilizada
o    Compilador usado
o    Sistema Operacional utilizado

Alguns exemplos de tempos exigidos para tarefas diversas:
  • ·    tfetch => tempo necessário para obter um dado (variável do programa) da memória
  • ·    tstore => tempo necessário para salvar um dado na memória (armazená-lo em uma variável)
  • ·    t+ => tempo necessário para executar uma operação de soma. Representações similares existem para as outras 3 operações elementares
  • ·    t< => tempo necessário para realizar a comparação lógica “menor que”. Existe a equivalente para “maior que”
  • ·    treturn => tempo necessário para finalizar a execução de um método/função, a partir do momento em que a instrução de código “return” é acionada
  • ·    t[.] => tempo necessário para calcular endereços quando trabalhando-se com “arrays”

Exemplos (Te = tempo de execução):

Exemplo 1: 

y = x;
Te = tfetch + tstore

Exemplo 2:

y = y + 1;
Te = 2(tfetch) + t+ + tstore

Primeiro exercício abordado em sala:

O seguinte código foi abordado em sala para calcularmos o tempo de processamento (funciona em C, C++, C# e Java):

// 1) int result = 0;
// 2) for (int i = 1; i <= n; ++i)
// 3) {
// 4)            result += i;
// 5) }
// 6) return result;

Obviamente, o código não está completo, sempre há código necessário tanto antes como depois dessas instruções, em qualquer linguagem, para poder compilar e executar tais instruções.

Vale a pena lembrar também que

result += i;

é uma forma abreviada de escrever

result = result + 1;

Vamos então ao cálculo do tempo de processamento. Resolverei primeiro da forma que o professor fez em sala, e depois, mostrarei uma forma alternativa que pensei aqui que, quando testando, dão o mesmo resultado, e achei bem mais simples do que o método do professor.

Professor:

// 1) tfetch + tstore
// 2) tfetch + tstore
// 2) [2(tfetch) + t<] * (n + 1)
// 2) [2(tfetch) + t+ + tstore] * n
// 4) [2(tfetch) + t+ + tstore] * n
// 6) tfetch + treturn
________________________________________________ +
[6(tfetch) + 2(tstore) + t< + 2(t+)] * n + 5(tfetch) + 2(tstore) + t< + treturn

Minha forma alternativa:

Para facilitar, vou criar variáveis de mais fácil manipulação para nós que estamos acostumados com Álgebra e Polinômios desde o ginásio (primeiro grau):

tfetch = x
tstore = y
t< = w
t+ = z
treturn = s
(n + 1) = a
n = b

Agora, basta trabalhar com x, y , w, z, s, a e b. Veja como fica:

// 1) x + y
// 2) x + y
// 2) (2x + w) * a
// 2) (2x + z + y) * b
// 4) (2x + z + y) * b
// 6) x + s
____________________ + (agrupando termos iguais)
2 (x + y)
a * (2x + w)
2b * (2x + z + y)
x + s
____________________ + (aplicando a distributiva, somando e agrupando os monômios similares)
(2x + 2y) + (2xa + aw) + (4bx + 2bz + 2by) + (x + s) =
3x + 2y + 2xa + aw + 4bx + 2bz + 2by + s (está resolvido)

Agora que a Álgebra foi realizada, trazendo novamente a nomenclatura técnica, fica assim:
3(tfetch) + 2(tstore) + 2[(n + 1)(tfetch)] + [(n + 1)(t<)] + 4 [n(tfetch)] + 2[n(t+)] + 2[n(tstore)] + treturn

Para testar os resultados, tanto o do professor quanto o meu, que visualmente não há indicações de serem equivalentes, basta escolher um valor para a variável “n” e verificar o que obtemos. Ambos dão o mesmo resultado, portanto são equivalentes, e vou mostrar isso escolhendo arbitrariamente o valor 4 para a variável “n”. Temos portando 2 resultados:

Professor:

[6(tfetch) + 2(tstore) + t< + 2(t+)] * n + 5(tfetch) + 2(tstore) + t< + treturn

Meu:

3(tfetch) + 2(tstore) + 2[(n + 1)(tfetch)] + [(n + 1)(t<)] + 4 [n(tfetch)] + 2[n(t+)] + 2[n(tstore)] + treturn

Testando a equivalência, para n = 4:

Em ambos os casos, obtemos
29(tfetch) + 10(tstore) + 5(t<) + 8(t+) + treturn

Conclusão:

Confesso que não entendi a última etapa do método do professor, e como preciso de um método para calcular isso (as provas chegarão um dia), acabei pensando na forma acima, que eu acredito simplificar o entendimento durante todo o processo, facilitando para não se perder durante as contas.

Eu acredito que ele fez da forma que fez porque ele quis agrupar o resultado de forma a ser visível através da fórmula

T(n) = t1 + t2n

sendo:
t1 = 5(tfetch) + 2(tstore) + t< + treturn
t2 = [6(tfetch) + 2(tstore) + t< + 2(t+)] * n

Esta é uma fórmula geral para tal tipo de cálculo dentro de um looping “for”. Caso fosse um looping “for” dentro de outro, também conhecido como “nested loop”, de acordo com o professor, a fórmula seria

T(n) = t1 + t2n2

Bom, é isso o que tenho a dizer no momento. Espero que o conteúdo acima sirva para os companheiros de sala e para todos que dependam deste entendimento.

Abraços!

No comments:

Post a Comment