Você está na página 1de 10

EP1 - Medindo tempo de execuo e contando operaes em mtodos recursivos.

Mrcio M. Ribeiro, Fbio Nakano e Flvio L. Coutinho

Objetivos Especficos
Treinar codificao e depurao de mtodos recursivos Verificar a aderncia entre tempo de execuo e quantidade de comparaes Comparar os programas dados como exemplo Codificar e comparar um algoritmo

Introduo
As principais caractersticas desejveis de algoritmos so: Fornecer o resultado correto; Rodar em tempo razovel (geralmente o mais rpido possvel) e usar quantidade de memria razovel (geralmente o mnimo possvel). O primeiro tem remete anlise de corretude do algoritmo. Assunto muito importante, mas fora do escopo da disciplina. O tempo de execuo de um programa pode ser medido diretamente. As linguagens de programao mais comuns tm funes (mtodos) que retornam o horrio (s vezes em nanossegundos), e com isso possvel obter o tempo que um programa levou para ser executado. Tambm possvel verificar quanta memria usada pelo programa (em Java, ver System.nanotime() e Runtime.totalMemory()). Note que essas medidas (experimentais) diretas esto sujeitas a variaes pois usual que vrios processos compartilhem os recursos do computador (basicamente memria e processador). Elas tambm dependem da arquitetura da mquina (p. ex. x86, ARM, ). Esta forma de anlise vlida, mas tem as restries inerentes anlise de dados experimentais e no prescindem do conhecimento da matemtica envolvida. Tcnicas que no requerem a anlise de dados experimentais, por isso mais simples, foram agrupadas e fazem parte da rea da computao chamada Anlise de Algoritimos. Seu processo central consiste em partir de um algoritmo ou programa, identificar uma varivel que sirva como indicador de tamanho do problema e extrair uma funo do tamanho do problema que seja proporcional ao tempo de execuo (ou quantidade de memria). Pode-se a partir disso: estudar o crescimento dessa funo e decidir se esse crescimento (do tempo de execuo ou uso de memria) aceitvel ou comparar algoritmos, extraindo a funo associada a cada um deles e compar-las, determinando qual algoritmo melhor. Este EP pretende explorar na prtica esses pontos. Para isso voc deve analisar os pacotes anexados, testar o exemplo, codificar e comparar os algoritmos de busca sequencial e binria.

Descrio sucinta dos pacotes fornecidos Nome SampleIt.java Sampler.java Fibo1.java Fibo2.java Compare.java Solution.java Problem.java Dados.java Profilable.java Printable.java doc Descrio mtodo main Roda o mesmo problema vrias vezes e armazena tempo de execuo e nmero de comparaes Exemplo: fibonacci O(2^n) Exemplo: fibonacci O(n) Conta as operaes de comparao Armazena uma soluo Armazena um problema Superclasse de Solution e Problem interface a que deve aderir a classe com mtodo a medir interface a que deve aderir a classe com algo a ser impresso. pasta contendo javadoc de todas as classes

O Exemplo A sequncia de Fibonacci, definida abaixo, pode ser computada com duas chamadas recursivas, como feito em Fibo1.java, ou com uma s (que retorna dois termos), como em Fibo2.java.

F ( n )=

(a) 0 n= 0 1 n =1 F ( n1 )+ F ( n 2) n > 1

A complexidade de tempo da primeira soluo exponencial, e a da segunda linear. Isto pode ser visto experimentalmente compilando e executando SampleIt instanciando Fibo1 ou Fibo2. No exemplo, utilizou-se Fibo1, compilou-se e executou-se obtendo: nakano@nakano:/media/E235-B919/icc2-2013/ep1-2013$ javac SampleIt.java nakano@nakano:/media/E235-B919/icc2-2013/ep1-2013$ java SampleIt 10 2603007 2304823 2312517 2300685 2388077 2314642 2641389 2404295 2066181 2084763 177 177 177 177 177 177 177 177 177 177

16 16 16 16 16 16 16 16 16 16 nakano@nakano:/media/E235-B919/icc2-2013/ep1-2013$ java SampleIt 20 334110623 298866674 298441314 298914356 298284145 298447443 298531572 298142288 298505452 298366857 21891 21891 21891 21891 21891 21891 21891 21891 21891 21891 26 26 26 26 26 26 26 26 26 26 nakano@nakano:/media/E235-B919/icc2-2013/ep1-2013$ Na primeira execuo calculou-se dez vezes o dcimo termo da sequncia, na segunda calculou-se para o vigsimo termo. O primeiro array corresponde ao tempo de execuo em nanossegundos, o segundo quantidade de comparaes e o terceiro mxima profundidade da pilha. Repetindo para diferentes tamanhos de problema, para Fibo1.java obtemos os grficos abaixo:

Tempo de execuo
70000000000 60000000000 50000000000
tempo (ns)

40000000000 30000000000 20000000000 10000000000 0 5 10 15 20 25 30 35


tamanho (n)

A B C D E F G H I J

Nmero de Comparaes
3000000 2500000
comparaes (n)

2000000 1500000 1000000 500000 0 5 10 15 20


tamanho (n)

A B C D E F G H I J 25 30 35

Profundidade mxima da pilha de execuo


40 35 30
profundidade (n)

25 20 15 10 5 0 5 10 15 20
tamanho (n)

A B C D E F G H I J 25 30 35

Para Fibo2.java obtemos os grficos abaixo:

Tempo de execuo
1800000 1600000 1400000 1200000
tempo (ns)

1000000 800000 600000 400000 200000 0 5 10 15 20


tamanho (n)

A B C D E F G H I J 25 30 35

Nmero de Comparaes
60 50
comparaes (n)

40 30 20 10 0 5 10 15 20
tamanho (n)

A B C D E F G H I J 25 30 35

Profundidade mxima da pilha de execuo


60 50
profundidade (n)

40 30 20 10 0 5 10 15 20
tamanho (n)

A B C D E F G H I J 25 30 35

A primeira parte de seu trabalho entender como funciona o cdigo (principalmente de Fibo1 e Fibo2) funciona, analisar e comentar os grficos acima. O produto a*n poderia ser escrito com a recorrncia: A : P ( a , n )=

0 n=0 (I) a + P ( a , n1 ) n > 0

Que resultado de um projeto por induo fraca, ou por 0 n= 0 P ( a , n )= P (a , n / 2 )+ P (a ,n / 2) n par P ( a ,n / 2)+ P ( a , n / 2)+ a n mpar que o resultado de um projeto por induo forte. A implementao pode ser feita usando uma varivel auxiliar, que armazena o resultado da chamada da funo e depois somada, ou por duas chamadas da funo, grosso modo, como apresentado abaixo: B int r=P(a,n/2); return r+r; C return P(a,n/2)+P(a,n/2)

A segunda parte de seu trabalho implementar A, B e C, fazer os experimentos, fazer os grficos, analisar e comentar. muito importante comparar B e C.

Especificao
Para o clculo, o inteiro a deve ser armazenado em um array de tamanho 1 em arrayValue de um objeto da class Problem. O inteiro n deve ser armazenado em intValue, como no exemplo (Fibo1 e 2). O resultado deve ser armazenado em intValue de um objeto da classe Solution, como em Fibo 1 e 2. Cada um dos algoritmos deve se chamar A.java, B.java e C.java, de acordo com as letras dadas acima e estar conforme a interface Profilable. Apenas o teste para a condio de parada da recurso deve usar um objeto da classe Compara. Caso haja mais de um teste para condio de parada, todos devem usar o mesmo objeto da classe Compara (espera-se que s haja um teste).

Testes
uma multiplicao, pessoal...

Avaliao
As solues sero avaliadas pela clareza e formatao do cdigo (indentao, comentrios, etc) e sua capacidade de resolver diferentes testes. Os relatrios sero avaliados por sua clareza, contedo e aderncia com o contedo da disciplina. Se a soluo causar erro ou exceo em determinado teste, receber zero nesse teste. Recebero zero as solues que no tiverem relatrio; no compilarem; estiverem fora da especificao (inclusive formato de entrega); tiverem modificado mtodos pr-existentes; forem copiadas (deteco por sistema automtico).

permitido criar novos mtodos dentro das classes que voc escrever, lembrando que a resposta tem que ser calculada com uma nica invocao de run(...).

Entrega
Os fontes A.java, B.java, C.java e o relatrio em formato PDF devem ser compactados juntos em um nico arquivo com formato zip ou rar. O nome de arquivo compactado deve ser seu nmero USP, sem prefixos ou sufixos. Por exemplo se seu nmero 1234567, ento seu arquivo deve ser 1234567.zip. Os arquivos java devem ter o cabealho: /****************************************************************/ /** ACH 2002 - Introducao a Ciencia da Computacao II **/ /** EACH-USP - Segundo Semestre de 2013 **/

/** **/ /** <turma> - <nome do professor> **/ /** **/ /** Primeiro Exercicio-Programa **/ /** **/ /** <nome do aluno> <numero USP> **/ /** **/ /****************************************************************/ A entrega deve ser feita atravs do Tidia at 21.09.2013. No sero aceitas entregas aps este dia.

Dicas
As classes fornecidas para este EP provavelmente sero usadas nos EPs subsequentes. Para evitar variaes na indentao em funo da relao entre espaos e TABs, recomenda-se que toda indentao seja feita com TABs e que se for possvel no editor de texto escolhido, estes sejam ajustados para exibir recuo equivalente a 4 espaos. Consulte a API do Java (http://docs.oracle.com/javase/1.6.0/docs/api/).

Apndice A - Recurso e pilha de execuo


O computador (ou sua abstrao, a Mquina Virtual Java) controla todas as invocaes de mtodo, de forma que quando um mtodo retorna, o programa volte ao ponto imediatamente posterior a sua invocao e com o escopo ajustado corretamente. A principal estrutura de controle de invocao chamada pilha de execuo. Uma pilha (em computao) funciona como uma pilha no mundo real, ou seja, vamos amontoando objetos (por exemplo papis) um sobre o outro. Quando quisermos desempilhar os papeis, o que conseguimos desempilhar primeiro o que empilhamos por ltimo. Dizemos que uma estrutura ltimo que entra, primeiro que sai (ingls LIFO - last in first out). Por exemplo, no cdigo abaixo:
1: class Exemplo { 2: public static void main (String[] args) { 3: System.out.println (teste(5)); 4: } 5: static int teste (int t) { 6: System.out.println (); 7: return t; 8: } 9: }

Quando o programa executado, o primeiro mtodo invocado main, logo ele o primeiro a entrar na pilha de execuo. Na linha 3 o mtodo println invocado, e, antes que este retorne, o mtodo teste invocado. Se imprimssemos os nomes dos mtodos na pilha de execuo entre as linhas 5 e 6, obteramos teste, println e main. Fazendo de teste um mtodo recursivo, obtemos:
1: class Exemplo { 2: public static void main (String[] args) { 3: System.out.println (teste(5)); 4: } 5: static int teste (int t) { 6: System.out.println (); 6a: teste (t); 7: return t; 8: } 9: }

A pilha de execuo, como qualquer outro objeto, tem tamanho limitado (a quantidade de memria do computador limitada). A cada invocao de teste ela ganha um elemento, logo, o mtodo teste vai ser invocado at que a pilha transborde. Quando isso ocorrer, ser lanada a exceo stack overflow, e a mquina virtual vai abortar o programa. Mtodos recursivos necessitam de uma condio de parada. Quando essa condio for verdadeira, a invocao do mtodo no feita, interrompendo a recurso. Por exemplo no clculo do produto por somas sucessivas: 1: class Produto {
2: 3: 4: 5: 6: 7: 8: 9: } public static void main (String[] args) { System.out.println (produto(5,7)); } public static int produto (int m, int n) { if (n==0) return 0; // condio de parada return m+produto(m,n-1); }

A primeira invocao de produto feita com os parmetros 5 e 7. A segunda feita com os parmetros 5 e 7-1=6. A terceira feita com os parmetros 5 e 7-2=5 e assim por diante, at que o parmetro n valha zero. Neste caso no h nova invocao e o mtodo retorna. Neste exemplo, as invocaes retornam sucessivamente, at que se retorne para main - quando impresso o valor final. Se quiser ver como funciona, acrescente prints: 1: class Produto {
2: 3: 4: 5: 6: 7a: public static void main (String[] args) { System.out.println (produto(5,7)); } public static int produto (int m, int n) { if (n==0) return 0; // condio de parada System.out.println (invocando);

7b: 7c: 7d: 8: 9: }

int r=produto(m,n-1); System.out.println (retornando); return m+r; }

Java permite visualizar a pilha de execuo na forma de um arranjo (array, vetor) de objetos da classe StackTraceElement. Esse array extrado do programa atravs do mtodo getStackTrace() [1]. Desta forma possvel verificar quantas invocaes recursivas foram feitas.

Você também pode gostar