Upload
others
View
0
Download
0
Embed Size (px)
Citation preview
Guião 3 de POO 2009/2010
1
Recursividade e Programação Gráfica Programação Orientada pelos Objectos
Guião 3, 6 de Abril de 2010 ([email protected])
Prazo de Submissão: 19 de Abril de 2010
Notas prévias O trabalho descrito neste guião deve ser submetido até às 23h55 do dia 19 de Abril de 2010. Para a primeira parte do guião, são fornecidas baterias de teste que deve usar para verificar a sua resolução, com o auxílio da ferramenta diff, antes de submeter o seu trabalho ao Mooshak. As primeiras cinco submissões de cada tarefa não penalizam a cotação da tarefa. No entanto, por cada submissão subsequente serão descontados 10% da cotação da tarefa. Na segunda parte do guião, as submissões não serão feitas no Mooshak mas sim no YouTube ou no Google Vídeo. Note ainda que, tal como em guiões anteriores, todas as classes implementadas devem fazer parte de um pacote poo.
Objectivos didáticos deste guião Neste guião, vamos exercitar dois aspectos importantes de programação, a recursividade e a programação gráfica, embora esta última de uma forma introdutória. Apesar de não ser referido explicitamente ao longo do guião, estamos a contar que crie testes unitários para ir verificando, passo a passo, a correcção das operações que está a implementar. Por outro lado, o trabalho do guião é complementar ao estudo destes assuntos no âmbito das aulas teóricas, bem como dos capítulos em causa no livro de referência da disciplina.
Parte I – Recursividade
Tarefa A – Recursividade simples e elegante Os algoritmos recursivos (ou recorrentes) têm como principio basilar a definição de algo em termos de si próprio. Tomemos como exemplo o factorial de um número inteiro. A sua formulação matemática, a seguir apresentada de um modo recorrente, conduz a uma solução computacional recursiva simples e elegante.
€
n !=n n −1( ) ! se n > 01 se n = 0
⎧ ⎨ ⎩
A sucessão de Fibonacci é outro exemplo parecido em termos de definição matemática versus programação recursiva. Recordando, uma sucessão de Fibonacci é uma sequência de números { 1, 1, 2, 3, 5, 8, 13, 21, 34, ... } definida matematicamente da seguinte forma:
Guião 3 de POO 2009/2010
2
€
Fib n( ) =Fib n − 2( ) + Fib n −1( ) se n > 2Fib 1( ) = Fib 2( ) =1
⎧ ⎨ ⎩
Como curiosidade, esta sequência é por vezes utilizada para modelar idealisticamente o crescimento de uma população de coelhos: Dois coelhos recém-‐nascidos, macho e fêmea, são colocados num campo agrícola. Ao fim de um mês, estão em condições de acasalar e no mês seguinte nasce um novo casal de coelhos. O processo reprodutivo mantem este padrão e, se não morrerem coelhos entretanto, a questão que se coloca é a de saber qual o número de coelhos existentes ao fim de um deteminado número de meses. Alguma ideia sobre este número ao fim de um ano?
Vamos então testar algumas soluções que envolvem técnicas recursivas. Antes de mais, convém lembrar os seguintes aspectos teóricos para a construção de soluções recursivas:
a. Como definir o problema em termos de um problema menor e do mesmo tipo? b. De que modo diminui o tamanho do problema, em cada chamada recorrente? c. Qual o caso particular do problema que pode servir para a paragem? Ou seja, o modo
como diminui o tamanho do problema, em cada chamada, assegura que o caso base é sempre atingido?
1. Como tarefa inicial, responda a cada uma das perguntas acima apresentadas tendo como enquadramento o problema do cálculo do factorial de um número inteiro.
2. Vamos agora criar um conjunto de classes e interfaces que nos permitam calcular e testar as sucessões matemáticas Factorial e Fibonacci. Propomos a seguinte organização:
A interface MathSequence caracteriza-‐se pelas seguintes operações:
• String getName() – obter o nome da sequência. • void setSequence(int n) – definir todos os elementos da sequência, do
primeiro, que corresponde ao índice 1, até ao elemento de índice n (s1, s2, ..., sn).
Guião 3 de POO 2009/2010
3
• void set(int indice) – definir o elemento da sequência na posição indice, sindice.
• long get(int indice) – obter o elemento da sequência na posição indice, sindice. • int getCounter() – obter o número de elementos da sequência. • String toStringSequence() – obter uma String com o conteúdo da sequência.
3. Implemente a classe abstracta AbstractMathSequence, sem esquecer que o método correspondente à operação set é abstracto. A sua implementação será da responsabilidade de cada uma das sub-‐classes. Os valores da sequência devem ser armazenados num vector.
4. Implemente agora as sub-‐classes Factorial e Fibonacci. Implemente dois métodos estáticos para devolver o valor da sequência para um dado n, ambos definidos recursivamente: um para o caso do factorial e outro para o caso da sucessão de Fibonacci, respectivamente, long factorial(int n) e long fibonacci(int n).
5. Vamos agora testar o factorial. Tal como aconteceu em guiões anteriores, os testes são invocados a partir de métodos estáticos numa classe Main. Comece com n=10 e depois com n=18. Crie testes unitários para ir verificando os resultados obtidos. Se acha que demorou muito tempo a calcular, nomeadamente no caso de n=18, tente implementar um novo método iterativeFactorial(int n) que torne a resposta mais rápida. Note que a definição recorrente facilita a tarefa de encontrar uma solução iterativa. Volte a testar, agora para n=13. Se tivesse utilizado um int para devolver o valor calculado no caso de n=13, será que teria obtido uma resposta correcta? Faça o teste. Escreva o seguinte código no corpo do programa principal e confirme o resultado da experiência.
System.out.println(Integer.SIZE + " " + Integer.MAX_VALUE); System.out.println(Long.SIZE + " " + Long.MAX_VALUE ); 6. Repita o ponto anterior mas considerando agora a sucessão de Fibonacci. Faça o teste para n=10 e n=40. O tempo de execução desta solução recursiva parece ser demasiado. Para analisar melhor o problema, altere o método fibonacci(int n) inserindo um System.out.println(“Invocando Fib(“+n+”)...”) na primeira linha do método. Se fizer para n=7, irá constatar o número de vezes que o método é chamado, a maioria das quais são repetidas. Cálculos redundantes é algo que um bom programador dispensa! Não se esqueça de implementar uma versão iterativa.
As experiências realizadas até agora permitem concluir que a recursividade é uma técnica de programação simples e elegante, mas que deve ser aplicada com alguns cuidados, nomeadamente se os factores tempo e espaço de memória forem cruciais.
7. Vamos agora submeter a tarefa A no Mooshak. Está, neste momento, em condições de realizar os testes, antes de submeter a tarefa ao Mooshak. O processo é análogo ao dos guiões anteriores. Você vai criar um zip com o seu código e, quando o submeter ao Mooshak, o Mooshak vai testar o seu programa com cada um dos testes existentes. Alguns testes estão disponibilizados no Moodle. Os restantes, são secretos, mas não trazem surpresas. Portanto, é da sua inteira responsabilidade garantir que, pelo menos, o seu programa passa nos testes
Guião 3 de POO 2009/2010
4
públicos. À semelhança de guiões anteriores, não se esqueça de utilizar o diff. Lembre-‐se que tem apenas 5 tentativas por tarefa, antes de começar a ser penalizado.
Aqui fica um pequeno excerto de um traço de execução, elucidativo do tipo de teste que se vai usar. Primeiro, aparece o seu input. Depois, a resposta esperada do seu programa.
Input: factorial 2 sequencia 10 factorial 0 fibonacci 2 fibonacci 5 fibonacci 17 sequencia 13 fibonacci 13 fim
Output: Sequencia inexistente. Factorial: 1 1 2 6 24 120 720 5040 40320 362880. Fibonacci: 1 1 2 3 5 8 13 21 34 55. 1 1 5 Sequencia inexistente. Factorial: 1 1 2 6 24 120 720 5040 40320 362880 3628800 39916800 479001600. Fibonacci: 1 1 2 3 5 8 13 21 34 55 89 144 233. 233 Fim.
Tarefa B – Recursividade eficiente A recursividade não é sinónimo per si de solução pouco eficiente. Consideremos por exemplo a ordenação de um vector. O método QuickSort (C. Hoare, 1961) usa uma estratégia recursiva e é, em princípio, o algoritmo mais rápido de ordenação. Por sinal, é este o algoritmo em que se baseia o método sort da classe utilitária Arrays do Java.
Vamos implementar um método similar, o da ordenação por fusão, assente no seguinte princípio: se dois vectores estiverem ordenados, a sua fusão ordenada originará um novo vector, também ele ordenado. Ou seja, uma estratégia recorrente para ordenar um vector será:
a. Ordenar a primeira metade. b. Ordenar a segunda metade. c. Fundir as duas metades.
8. Implemente uma classe MergeSort para ordenar um vector de elementos. Aconselhamos desde já a consulta do livro adoptado na disciplina. A ordenação pode envolver qualquer tipo de objecto, desde que a respectiva classe desse objecto implemente o método compareTo da
4391785
4391
785
43
91
78
5
4
3
9
1
7
8
34
19
78
1349
578
1345789
Guião 3 de POO 2009/2010
5
interface Comparable. Por exemplo, pode ser um inteiro, uma string ou até um objecto da classe Date.
9. Teste o funcionamento da classe, lendo uma sequência de números inteiros e escrevendo os valores ordenados. Faça o mesmo para strings.
10. Vamos agora resolver o seguinte problema: Um grupo de alunos pretende constituir uma lista candidata a eleições para a Associação de Estudantes. A lista em causa, com 6 pessoas, deve ser constituída em número igual por alunos e alunas. Antes da escolha final da lista a propor, os alunos vão analisar detalhamente todas as hipóteses (entenda-‐se combinações matemáticas) possíveis, dentro do universo de candidatos. O número de candidatos de cada grupo não é superior a 6. Para facilitar essa tarefa, cada uma das listas possíveis é ordenada por ordem alfabética. Pense numa solução para o problema e implemente-‐a. Utilize também o método de ordenação implementado anteriormente.
11. Submeta a sua solução para este problema no Mooshak, incluindo a respectiva classe Main. A formatação dos comandos a utilizar é a seguinte:
Comando Acção m string Adiciona um aluno (String) ao conjunto de candidatos f string Adiciona uma aluna (String) ao conjunto de candidatos listas Apresenta todas as listas possíveis, uma por linha, e ordenadas. A ordem
pela qual aparecem as listas também é ordenada alfabeticamente, considerando para o efeito que cada uma das listas também é uma string (comparação lexicográfica do método string.compareTo(...) ) . Se os dados existentes não forem suficientes, o programa escreve Dados insuficientes.
fim Termina o programa, escrevendo Fim.
Aqui fica um exemplo de sessão, primeiro o conteúdo do ficheiro de input e depois o resultado gerado.
Input: m cardozo f luciana f nereida listas m liedson m dimaria m hulk f carolina listas fim
Output: Dados insuficientes. cardozo, carolina, dimaria, hulk, luciana, nereida. cardozo, carolina, dimaria, liedson, luciana, nereida. cardozo, carolina, hulk, liedson, luciana, nereida. carolina, dimaria, hulk, liedson, luciana, nereida. Fim.
Guião 3 de POO 2009/2010
6
Tarefa C – Pesquisa exaustiva de soluções Em certos casos, é necessário criar um mecanismo recorrente de pesquisa de várias soluções, também conhecido por backtracking. Em geral, não é fácil apresentar uma solução iterativa, pelo menos para programadores pouco experientes. De modo a ilustrar esta metodologia, vamos resolver um puzzle associado ao xadrez.
Uma rainha colocada numa casa domina a linha, a coluna e as duas diagonais dessa casa. Pretende-‐se determinar o modo de colocação de 8 rainhas não dominadas num tabuleiro de xadrez. As diagonais a ter em consideração são 30, sendo 15 delas caracterizadas pela soma linha+coluna (declive positivo) e 15 pela diferença linha-‐coluna (declive negativo). Considere que o canto superior esquerdo do tabuleiro de xadrez é a casa (linha=0, coluna=0).
12. Construa uma solução para o problema acima indicado. Existem 92 soluções possíveis. Como pretendemos testar no Mooshak, vamos estabelecer uma regra adicional: em cada momento, os testes de colocação da rainha seguem a ordem: linha 0, linha 1, ... linha 7 e, para cada linha, coluna 0, coluna 1, ... coluna 7. Apresenta-‐se a seguir o algoritmo que suporta a resolução deste puzzle.
Ao tentar colocar a rainha numa determinada linha i
Teste para cada coluna j:
Se a casa i,j estiver livre então
A rainha é colocada nessa casa e de seguida tentasse colocar a rainha na linha seguinte, i+1. Se por hipótese esta era a última linha, isso significa que foi encontrada uma solução e nesse caso não se avança para a linha seguinte.
Processo de backtracking: Retira-se a rainha da casa i, j para tentar encontrar outra solução.
Em termos de organização de classes e interfaces, convém ter presente como referência algumas classes utilizadas no guião 2, nomeadamente as relativas ao conceito de jogo e posição.
13. Submeta a sua solução para esta tarefa no Mooshak, incluindo a respectiva classe Main. Aqui fica um exemplo de sessão, primeiro o conteúdo do ficheiro de input e depois o resultado gerado.
Input: rainhas 100 rainhas 1 rainhas 4 fim
Guião 3 de POO 2009/2010
7
Output: Solucao inexistente. X------- ----X--- -------X -----X-- --X----- ------X- -X------ ---X---- X------- ------X- ----X--- -------X -X------ ---X---- -----X-- --X----- Fim.
Parte II – Programação Gráfica Nesta parte vamos incluir uma vertente gráfica nos nossos programas. Apesar de já termos utilizado primitivas gráficas no primeiro guião, agora vamos tentar simular graficamente os resultados obtidos nos programas. Tal como nesse guião, vamos utilizar um pacote de primitivas gráficas do Java, o java.awt, nomeadamente a classe Graphics2D. Antes de avançar convém reler os apontamentos teóricos e o livro de referência sobre este assunto. Pode ainda consultar um tutorial sobre a API Java 2D, nomeadamente o que se refere à classe Graphics2D. Entre outras coisas, esta API permite desenhar linhas, rectângulos e outras formas geométricas, com ou sem preenchimento da área interior, texto, imagens, etc. O tutorial referido encontra-‐se em:
http://java.sun.com/docs/books/tutorial/2d/index.html
Tarefa D – Gráficos com o “puzzle das rainhas” e fractais simples Esta tarefa consiste em dois trabalhos gráficos e vale dois pontos. O trabalho a desenvolver não é para ser submetido no Mooshak mas sim para mostar a sua execução em vídeo. Para tal, sugere-‐se a utilização da ferramenta Camtasia, a qual se encontra disponível em http://www.camtasia.com/download/default.asp. Note que pode utilizar outra ferramenta para o efeito se assim o entender.
Trabalho 1 14. O primeiro trabalho visa a implementação de uma versão gráfica do programa anterior sobre o puzzle das rainhas. Agora, pretende-‐se apenas visualizar uma determinada solução do puzzle. Para isso, o programa deve permitir ler um inteiro de 1 a 92, correspondente à solução a visualizar graficamente. Apelamos à sua criatividade. No vídeo, deve ainda ser visível uma String com o seu nome e número de aluno e não pode ter uma duração superior a 30 segundos.
Guião 3 de POO 2009/2010
8
Como hipótese inicial de trabalho, apresentamos o seguinte diagrama de classes:
Trabalho 2 Para terminar o guião, vamos desenhar fractais. De uma forma simplista, um fractal é qualquer padrão que revela grande complexidade à medida que cresce. Em termos gráficos, um fractal representa a noção visual de “mundos dentro de mundos”. Em muitos casos, os fractais são utilizados para descrever (aproximadamente) objectos do mundo real como por exemplo nuvens, montanhas, linhas de costa, folhas de árvores, etc. O conjunto de fractais mais conhecido é o chamado conjunto de Mandelbrot, do qual se podem ver os dois exemplos seguintes.
14. Vamos implementar fractais mais simples, a família de fractais denominada Koch Snowflake. A sua definição recursiva é a seguinte:
a. Começa-‐se com um triângulo equilátero. b. Cada lado é substituido por 4 segmentos de recta de igual comprimento, formando
uma estrela orientada para o exterior. c. O processo repete-‐se.
Considere mais um fractal desta família (Koch Anti-‐snowflake). Agora, começa-‐se com um triângulo equilátero relativamente grande e vai-‐se aplicando a mesma estratégia mas orientando a estrela para o interior.
Guião 3 de POO 2009/2010
9
As figuras seguintes demonstram o funcionamento dos algoritmos em causa.
15. Implemente uma classe que permita gerar os dois fractais da família Koch Snowflake. No caso do primeiro fractal, pode introduzir uma variante, em que cada lado triplica o seu comprimento de iteração para iteração. Utilize cores do modo a melhorar o impacto visual dos fractais. Acrescente uma linha de texto indicando a iteração corrente e o número de segmentos de recta distintos representados na figura gerada. Introduza um valor temporal que permita “parar” a execução do programa entre iterações, devendo também ser visível uma string com o seu nome e número de aluno. O vídeo, contendo imagens dos dois fractais, não pode ter uma duração superior a 1 minuto.
16. Junte os dois vídeos num único vídeo. O seu filme de demonstração deve ser colocado no YouTube (http://www.youtube.com/) ou no Google Vídeo (http://video.google.com/). 17. Deverá agora submeter ao Mooshak o seu código fonte completo com todos os passos que conseguiu implementar desta tarefa D. Tal como no primeiro guião, o corpo da função main do programa principal deve estar comentado, ou seja, o Mooshak não vai executar o seu programa. Este pedido está relacionado com a utilização do sistema Moss para eventual detecção de plágios.
18. Para que o professor responsável pelo seu turno prático possa avaliar o seu trabalho, deve entregar no Moodle um ficheiro zip com um documento em formato pdf em que, para além da sua identificação completa, contenha o endereço http do local onde está disponível o seu filme de demonstração.