FACULDADE FARIAS BRITO CIÊNCIA DA COMPUTAÇÃO RODSON COELHO DOS REIS CONSTRUÇÃO DE UMA FERRAMENTA DIDÁTICA PARA ENSINO DE COMPLEXIDADE ALGORÍTMICA Fortaleza 2011 RODSON COELHO DOS REIS CONSTRUÇÃO DE UMA FERRAMENTA DIDÁTICA PARA ENSINO DE COMPLEXIDADE ALGORÍTMICA Monografia apresentada para obtenção dos créditos da disciplina Trabalho de Conclusão do Curso da Faculdade Farias Brito, como parte das exigências para graduação no Curso de Ciência da Computação. Orientador: MSc. Murilo Eduardo Ybanez Nascimento. Fortaleza 2011 CONSTRUÇÃO DE UMA FERRAMENTA DIDÁTICA PARA ENSINO DE COMPLEXIDADE ALGORÍTMICA Rodson Coelho dos Reis NOTA: Data: ____/____/_______ BANCA EXAMINADORA ______________________________ MSc. Murilo Eduardo Ybanez Nascimento (Orientador) ______________________________ MSc. Daniel Matos Alves (Examinador) ______________________________ MSc. Maikol Magalhães Rodrigues (Examinador) FINAL (0-10): ______ AGRADECIMENTOS Ao Orientador Prof. Murilo pela paciência e dedicação no trabalho, o que permitiu que o projeto fosse concluído com qualidade. Aos Professores: Paulo Lemos, Cleiton, Fláudio, Othon, Sérgio, Maikol, Jarbas, Sales, Façanha, Wagner, Mateus, Azevedo e Brayner pela formação adquirida durante o curso. Aos meus pais Maria Dalva e Rosendo Lopes (in memoriam) pela educação, amor, apoio e ensinamentos que foram base para formação do caráter e para transpor obstáculos da vida. RESUMO Computadores não possuem recursos ilimitados, por isso, é necessário escrever algoritmos que utilizem esses recursos de forma sensata. A análise da complexidade de algoritmos possibilita desvendar a evolução dos custos de execução de um algoritmo em função do tamanho da entrada. Por se tratar de uma área que envolve conceitos matemáticos não triviais, a utilização de ferramentas didáticas adequadas pode auxiliar o professor na passagem do conhecimento e os alunos na fixação dos conceitos. Este trabalho busca o desenvolvimento de um software que suporte a definição de algoritmos, conte a quantidade de execuções de um certo conjunto de operações indicadas pelo usuário para diferentes tamanhos de entradas e apresente as curvas que representam as funções de complexidade, contribuindo para o ensino e a aprendizagem da análise de complexidade de algoritmos. Palavras chave: complexidade, algoritmo, desenvolvimento, software, educacional. SUMÁRIO 1. INTRODUÇÃO................................................................................................................... 13 2. INFORMÁTICA NA EDUCAÇÃO ...................................................................................... 15 2.1 Critérios para Avaliação de Software Educacional .......................................................... 17 3. 2.1.1 Critérios para Avaliação de Qualidade de Software ..................................................... 18 2.1.2 Critérios para Avaliação da Interface de Software ....................................................... 19 2.1.3 Critérios para Avaliação do Software quanto aos Resultados de Aprendizagem ............ 20 METODOLOGIA PARA ANÁLISE DE ALGORITMOS...................................................... 22 3.1 Pseudocódigo .............................................................................................................. 23 4. 3.2 Contagem de Operações Primitivas ............................................................................... 24 3.3 Análise Assintótica ...................................................................................................... 26 3.3.1 Notação O ................................................................................................................ 27 3.3.2 Notação Ω ................................................................................................................ 28 3.3.3 Notação Θ ................................................................................................................ 29 DESENVOLVIMENTO DA APLICAÇÃO........................................................................... 30 4.1 Requisitos do Sistema ...................................................................................................... 30 4.1.1 .................................................................................................................................... 31 Requisitos Funcionais........................................................................................................... 31 4.1.2 4.2 Requisitos não Funcionais ......................................................................................... 31 Arquitetura ..................................................................................................................... 32 4.2.1 Modelagem Arquitetural ............................................................................................ 33 4.3 Ambiente de Desenvolvimento......................................................................................... 38 4.4 Tecnologias Empregadas ................................................................................................. 38 4.4.1 Reflection ................................................................................................................. 38 4.4.2 Annotation................................................................................................................ 40 4.4.3 ClassLoader.............................................................................................................. 41 4.4.4 JSyntaxPane ............................................................................................................. 42 4.4.5 JFreeChart ................................................................................................................ 43 4.4.6 PDFHelp .................................................................................................................. 44 5. UTILIZAÇÃO DA FERRAMENTA ..................................................................................... 45 5.1 Interface ......................................................................................................................... 45 5.2 Palavras Reservadas ........................................................................................................ 48 5.3 6. 7. Exemplos........................................................................................................................ 50 5.3.1 Exemplo 1 ................................................................................................................ 50 5.3.2 Exemplo 2 ................................................................................................................ 52 AVALIAÇÃO DA FERRAMENTA NO CONTEXTO EDUCACIONAL ............................... 58 6.1 Questionário ................................................................................................................... 58 6.2 Resultados ...................................................................................................................... 59 CONCLUSÃO.................................................................................................................... 62 REFERERÊNCIAS BIBLIOGRÁFICAS...................................................................................... 64 ANEXO A – ALGORITMO COMPLEMENTAR ......................................................................... 67 LISTA DE FIGURAS Figura 1 - Exemplo de Pseudocódigo do Algoritmo Bolha ............................................................243 Figura 2 - Notação O ..................................................................................................................276 Figura 3 - Notação Ω ................................................................................................................... 27 Figura 4 - Notação Θ ..................................................................................................................298 Fi gura 5 - Diagrama de ati vidade da lógi ca geral da apli cação........................................................321 Fi gura 7 - Diagrama de Sequência da Aplicação ............................................................................376 Fi gura 8 - Exemplo code-completion no Netbeans........................................................................398 Fi gura 9 - Exemplo de instância de classe e exe cução de método via Refle ction. ............................398 Fi gura 10 - Hierarquia do ClassLoader. Fonte(http://www.de veloper.com/img/arti cles/2003/08/14/cl _hierarchy.gif) ...............................410 Fi gura 11 - JSyntaxPane ............................................................................................................... 41 Fi gura 12 – Ilustra çãoJ Linechart (JFree Cha rt)...............................................................................432 Fi gura 13 - Inte gração do PDFHelp no Netbeans ...........................................................................443 Fi gura 14 - Tela principal da aplicação.........................................................................................465 Fi gura 15 - Help da aplicação.......................................................................................................476 Fi gura 16 - Escri ta da e xpressão @TamanhoEntrada .....................................................................498 Fi gura 17 - Escri ta da e xpressão @contar...................................................................................... 49 Fi gura 18 - Ge ração das entradas Exemplo 1 ................................................................................. 49 Fi gura 19 - Contagem das operações Exemplo 1............................................................................ 50 Fi gura 20 - Gráfi co Exemplo 1....................................................................................................... 51 Fi gura 21 - Contagem das operações Exemplo 2...........................................................................532 Fi gura 22 - Ge ração das entradas Exemplo 2 ................................................................................532 Fi gura 23 - Gráfi co Exemplo 2......................................................................................................543 Fi gura 24 - Contagem das operações Exemplo 3...........................................................................554 Fi gura 25 - Ge ração das entradas Exemplo 3 ................................................................................565 Fi gura 26 - Gráfi co Exemplo 3......................................................................................................565 LISTA DE TABELAS Tabela 1 - Tamanho do Problema x Tempo de Execução ................................................................ 26 Tabela 2 - Descrição das classes do sistema................................................................................... 35 Tabela 3 - Pe rguntas x Cri térios .................................................................................................... 59 Tabela 4 - Resultado da a valiação do software .............................................................................. 60 LISTA DE ABREVIATURAS E SIGLAS API Application Programming Interface IDE Integrated Development Environment JRE Java Runtime Environment JVM Java Virtual Machine MBTI Myers Briggs Type Indicator MVC Model View Controller 13 1. INTRODUÇÃO Com a disseminação do computador e da Internet, a utilização de softwares como instrumento de aprendizado vem ganhando cada vez mais espaço no meio educacional. Esses softwares se utilizam da grande capacidade de armazenamento, conectividade e processamento disponível nos computadores atuais para apresentar informações de maneiras inovadoras e proporcionar experiências que podem ser utilizadas para fixar e ampliar os conhecimentos em sala de aula. Os softwares educativos podem auxiliar no processo de aprendizagem de várias formas, valendo-se de recursos como animações, jogos, simulações, dentre outros. No caso dos cursos de computação, ferramentas profissionais também são comumente usadas no ensino das disciplinas. Um exemplo é a utilização dos Ambientes de Desenvolvimento Integrados (Integrated Development Environment - IDE) pelos alunos para a codificação e execução de algoritmos, como parte das práticas de lógica de programação e desenvolvimento de sistemas. Uma disciplina que poderia se beneficiar mais do uso de ferramentas didáticas como apoio ao ensino é a análise da complexidade de algoritmos. O estudo da complexidade de algoritmos se propõe a investigar o comportamento do custo de execução, seja em termos de processamento ou memória, à medida que os dados de entrada aumentam. Quando o conjunto dos dados de entrada é pequeno, qualquer algoritmo pode ser usado para resolver um problema, porém, à medida que o tamanho da entrada cresce a eficiência do algoritmo se torna um fator relevante. 14 Por esta razão, um conhecimento sólido sobre análise de complexidade de algoritmos é fundamental para a formação de um profissional de Ciência da Computação, por se tratar de uma ferramenta que permite avaliar a eficiência da estratégia utilizada na codificação dos algoritmos e, em última instância, determinar a aplicabilidade dos mesmos na solução de problemas reais. O presente trabalho tem como objetivo definir e implementar um software que seja um facilitador para professores, como um instrumento adicional a ser utilizado em sala de aula, e para alunos, como uma ferramenta para exercitar os conhecimentos adquiridos, no que tange à análise da complexidade dos algoritmos. Por outro lado, um bom software educacional deve exibir certas características importantes tais como: uma boa usabilidade; interface clara e objetiva; recuperação e notificação de erros de utilização e disponibilidade de um módulo de ajuda para o usuário. O trabalho está dividido em sete capítulos. O primeiro capítulo faz uma breve introdução sobre o trabalho. No segundo capítulo, encontra-se alicerce teórico sobre softwares educacionais e os critérios de avaliação de uma aplicação no meio educacional. O terceiro capítulo apresenta uma metodologia para análise de algoritmo e fundamentos teóricos para representação dos custos dos algoritmos através de notações. O quarto capítulo descreve os fatores que guiaram o desenvolvimento da aplicação, tais como: requisitos, arquitetura, ambiente de desenvolvimento e tecnologias utilizadas. No quinto capítulo será ilustrado o funcionamento da aplicação através de exemplos de algoritmos e validação dos resultados obtidos com a metodologia para cálculo de complexidade. O sexto capítulo descreve um processo de avaliação da ferramenta através de um questionário e os resultados de avaliação. O último capítulo exibe as conclusões finais do projeto, bem como sugestões para trabalhos futuros. 15 2. INFORMÁTICA NA EDUCAÇÃO A informática educativa é uma área da informática que trata da utilização de computadores como instrumento de aprendizado, maximizando a difusão do conhecimento e facilitado a assimilação de um conteúdo (SANTOS; COSTA, 2005). Porém, de acordo com Zambalde e Alves (2002 apud SANTOS et COSTA, 2005), a inserção da informática no segmento educacional provoca mudanças tanto estruturais quanto comportamentais. A mudança estrutural ocorre devido à necessidade de se obter todo o equipamento necessário para informatizar um ambiente. A mudança comportamental refletese na maneira de ensinar, aprender e adaptar-se a uma forma complementar de ensino. Lucena (on-line) relata que havia um grande receio por parte dos professores de que o uso do computador, como ferramenta de ensino, viesse a substituí-los em sala de aula. Mas, atualmente há uma consciência sobre o emprego do computador devido à sua crescente popularidade, permitindo ser um item complementar em um processo extenso de aprendizagem. A utilização do computador como meio de transmissão da informação ao aluno reflete-se na informatização dos processos de ensino vigentes, o que facilita a adoção do computador sem romper a dinâmica do meio educacional (VALENTE, 1997). Valente (on-line) relata que capacitar o professor para utilizar os recursos oferecidos pela informática como instrumento de ensino e aprendizagem não significa incrementar o seu conhecimento com técnicas ou conhecimento de informática. É importante que o professor saiba como integrar o computador à sua disciplina para tirar o maior proveito 16 dessa tecnologia. É necessário que o educador conheça o processo de aprendizagem, como ele acontece e como intervir de maneira efetiva na relação aluno-computador. O autor expõe ainda que o professor de uma disciplina deve ter conhecimento sobre os potenciais educacionais do computador e ser capaz de distribuir adequadamente atividades tradicionais de ensino com atividades que exigem o uso do computador. Em sala de aula, as atividades informatizadas tanto podem ter um intuito de dar continuidade à transmissão da informação ao aluno quanto propiciar melhores condições para construção do conhecimento através de ambientes de aprendizagem. Segundo Papert (1994 apud SANTANCHÉ et TEIXEIRA, 1999), a utilização do computador no meio educacional se classifica em duas metodologias: instrucionista e construcionista. De acordo com Borges (2002), “o instrucionismo é a base do sistema adotado na maior parte das escolas atuais”. A metodologia instrucionista fundamenta-se no ato de ensinar por meio da transmissão da informação ao aluno (VALENTE, 1997). Nesse contexto, o computador é um potencializador dessa metodologia, devido a sua grande capacidade de programar instruções a serem passadas aos alunos. No caso, o computador é programado para ensinar o aluno, funcionando como um guia para um conteúdo. Além disso, sistemas podem validar as respostas fornecidas pelo aluno, livrando o professor de correções de exercícios e provas. No paradigma instrucionista, o professor não precisa de uma formação profunda em informática aplicada à educação. Basta o professor ser treinado para conhecer o funcionamento do computador e do software para utilizar como recurso adicional no ensino de uma disciplina (VALENTE, on-line). O presente trabalho inspira-se no conceito instrucionista. A metodologia construcionista baseia-se na construção do conhecimento através do computador (PAPERT, 1986 apud VALENTE 1997). Nesse sentido, a construção do conhecimento ocorre quando um aluno constrói um objeto de seu interesse, como por exemplo, um programa de computador. O conhecimento é adquirido pela prática da construção do objeto e pelo envolvimento afetivo do aluno ao construir algo de seu interesse. Quando o aluno interage com o computador ele está manipulando conceitos que contribuem 17 para o seu desenvolvimento cognitivo, da mesma forma que ele adquire conhecimento ao interagir com objetos do mundo real. Nesse caso, o computador é programado para ser ensinado (VALENTE, 1997). No paradigma construcionista, o professor precisa conhecer aspectos técnicos sobre a ferramenta computacional, como, por exemplo, a linguagem de programação e o banco de dados, conhecer sobre o processo de aprendizagem e ter uma noção sobre aspectos sociais e afetivos que influenciam a aprendizagem. Todo esse conhecimento não é adquirido através de treinamento, mas sim através de um processo de formação (VALENTE, on-line). Dada uma visão geral sobre a informática na educação, a próxima subseção descreverá as características de um software no âmbito educacional. 2.1 Critérios para Avaliação de Software Educacional Um software educacional é um programa de computador cujo propósito é atender os objetivos educacionais previamente estabelecidos. Para atingir esse objetivo, é necessário que esse software seja desenvolvido em conjunto com especialistas da informática e da educação, deixando-o efetivo e de acordo com os propósitos pedagógicos (ZAMBALDE et ALVES, 2002 apud SANTOS et COSTA, 2005). Lucena (on-line) cita que geralmente os softwares educacionais são desenvolvidos por equipes que não são interdisciplinares e que, na maioria das vezes, são compostas por engenheiros de software. Tal situação pode comprometer o trabalho dos educadores por conta do uso de um produto que não corresponde às suas expectativas pedagógicas. A autora ainda expõe que a avaliação de um software educacional se baseia em três critérios fundamentais: qualidade, interface e os resultados da aprendizagem. O primeiro critério refere-se à qualidade do software influenciada pela normatização dos processos de desenvolvimento. O segundo critério refere-se à capacidade de comunicação do software. O terceiro critério foca nos resultados e consequências da aprendizagem do usuário. 18 2.1.1 Critérios para Avaliação de Qualidade de Software A qualidade de software é a satisfação de requisitos funcionais e de desempenho explicitamente declarados, normas de desenvolvimento explicitamente documentadas e características implícitas que são esperadas em todo o software desenvolvido profissionalmente. (PRESSMAN, 2006, p. 349). Os requisitos funcionais são aqueles que descrevem as funcionalidades do sistema. Os requisitos de desempenho se baseiam em fatores como: tempo de resposta, precisão, capacidade, etc. As normas de desenvolvimento são os critérios que guiam o desenvolvimento do software. As características implícitas são atributos esperados de um software tais como manutenibilidade e confiabilidade. Todas essas necessidades são indispensáveis para propiciar a qualidade de qualquer software, inclusive de programas educacionais. No contexto específico de softwares educacionais, Lucena (on-line) elaborou um conjunto de perguntas que expressam critérios para avaliação de qualidade de software, a fim de facilitar o trabalho do educador ou avaliador: 1. O software reage ao usuário de maneiras previsíveis? 2. O software é simples com relação ao aprendizado das funções essenciais? 3. O software é visualmente atrativo com relação à apresentação do conteúdo? 4. O software permite localizar instruções sobre uso (help) independentemente da situação em que o usuário se encontra? 5. O software apresenta erros eventuais ou intermitentes? 6. O tempo entre intervenções do usuário é tolerável? 7. O software reage adequadamente a erros grosseiros de utilização? 8. O software prevê procedimentos de recuperação para situações de falhas? Portanto, acredita-se que essas perguntas simplificam aspectos teóricos relacionados aos critérios de qualidade de software educacional, facilitando a avaliação de um professor que não tenha disponibilidade, comprometimento ou acessibilidade à literatura correspondente (LUCENA, on-line). Depois de estabelecidas as questões de qualidade de software, a subseção a seguir abordará critérios para avaliação da interface homem-máquina. 19 2.1.2 Critérios para Avaliação da Interface de Software “O projeto de interface com o usuário cria um meio efetivo de comunicação entre o ser humano e o computador.“ (PRESSMAN, 2006). O projeto identifica os objetos e as ações de interface e depois cria um padrão de tela que será a base para um protótipo de interface com o usuário. A elaboração da interface é importante, pois molda a percepção do software por parte do usuário. Durante o processo de construção da interface para comunicação homemcomputador, é necessário levar em conta os objetivos do software e os requisitos do cliente (LUCENA, on-line). Por exemplo, modelar um software educacional para usuários do ensino fundamental é diferente de modelar um software para acadêmicos. A autora ainda cita que é preciso antes definir objetivos e os perfis físico, psicológico e intelectual dos alunos, para depois definir os aspectos do software educativo. A escala apresentada no tipo de indicador Myers Briggs (Myers Briggs Type Indicator – MBTI) descreve fatores psicológicos que influenciam na interação do homem com a máquina (SHNEIDERMAN, 1987 apud LUCENA, on-line): 1. Usuários extrovertidos apreciam variedade de ação e estímulos externos; usuários introvertidos trabalham bem sozinhos e desenvolvem cuidadosamente suas ideias; 2. Usuários perceptivos gostam de novas situações, porém demonstram indecisão em suas ações; outros, entretanto, planejam cuidadosamente suas ações, levados pelo julgamento e procuram finalizar suas tarefas; 3. Usuários sentimentais transferem sua afetividade para a máquina, procurando resolver os problemas apresentados pelo programa, numa tentativa de agradar e de receber recompensas; 4. Usuários racionais colocam as funções em ordem, não se importando com um tratamento impessoal. Em se tratando da construção de interfaces para usuários de ambos o sexos, há algumas questões a considerar (LUCENA, on-line): 1. Uma interface pode ser perfeita para um determinado grupo de usuários e pode não servir para outro; 20 2. Uma interface pode ser suficiente para uma classe de tarefas e não para outras; 3. Uma interface pode ser utilizada em determinado equipamento e periféricos e não em outros. A interface deve ajudar no processo de comunicação e cognição, permitindo que o usuário obtenha melhores resultados em sua área e atinja objetivo eficientemente e eficazmente. Nesse sentindo, a interface deve (STAHL, 1988 apud LUCENA, on-line): 1. Reduzir a ansiedade e o medo natural de manipulação da máquina. Muito influem, para tal, os sistemas de ajuda e de consulta amigáveis, bem como uma linguagem acessível e telas atraentes; 2. Demonstrar uma evolução eficiente e gradativa de mensagens e graus de complexidade em sua arquitetura de apresentação. Este fator contribui para a agilização do processo de interação; 3. Garantir a esperada retroalimentação (feedback ) com estratégias inteligentes e abertas a informações com assistência a decisões dos usuários. Através de respostas e perguntas do usuário, possibilitar um diagnóstico em relação aos pré-requisitos e rapidez do andamento do programa. Entendida a importância das interfaces para usuários comuns e usuários do meio educacional, a seguinte subseção descreve fatores para compreender os resultados de aprendizagem que o software propicia. 2.1.3 Critérios para Avaliação do Software quanto aos Resultados de Aprendizage m É de se esperar que os resultados de aprendizagem na utilização do software dependam de qual abordagem o software se encaixa: instrucionista ou construcionista. Na abordagem instrucionista, os resultados podem ser previstos e a avaliação da aprendizagem pode ser passada ao aluno em tempo real. Na abordagem construcionista, a capacidade de cognição do aluno é trabalhada mais intensivamente, já que ele constrói objetos e faz reflexões. Assim, os resultados de aprendizagem são imprevisíveis. 21 Entretanto, de acordo com pesquisas sobre a construção do conhecimento e compreensão humana, existem alguns fatores que influenciam o desenvolvimento mental (GAGNÉ, 1986 apud Lucena on-line): a) memória; b) capacidade de solucionar problemas. Um software educativo deve dispor às informações visando distribuir as proposições da tela, de tal sorte que não sobrecarregue a memória do aluno. Uma tela de software eficiente deve limitar a quantidade de informações para não comprometer o aprendizado do usuário. Na medida em que o ser humano vivencia situações ou problemas, ele procura adotar estratégias mais gerais, ou seja, do seu julgamento, em busca de soluções. Por isso, softwares devem ser minuciosamente analisados quanto à condução da solução dos problemas para não gerar respostas inadequadas e, consequentemente, não levar o usuário para uma estratégia equivocada. Além disso, o software deve ser desenvolvido para corresponder ao nível de leitura e compreensão do aluno, caso contrário o impedirá de adquirir a compreensão desejada. 22 3. METODOLOGIA PARA ANÁLISE DE ALGORITMOS A metodologia para análise de algoritmo implementada na ferramenta baseia-se no cálculo do tempo de execução de um algoritmo. Para realizar medições é preciso executar um algoritmo sobre diferentes entradas com tamanhos distintos. O resultado pode ser visualizado em um gráfico, onde o eixo das abscissas corresponde às entradas e o eixo das ordenadas corresponde ao tempo de execução ou espaço de memória utilizado. A complexidade temporal mede a quantidade de tempo necessário para executar um algoritmo com entrada de tamanho n (ZIVIANI, 2004). O problema dessa medição é que o desempenho da execução do algoritmo é sensível ao software e ao hardware, podendo gerar valores distintos em vários computadores. A complexidade espacial mede o espaço de memória necessário para a execução de um algoritmo com entrada de tamanho n (TOSCANI; VELOSO, 2005). O presente trabalho utiliza-se dessa técnica, mais especificamente, do levantamento da quantidade de instruções relevantes do algoritmo para fins de determinação do tempo de execução, cujo intuito consiste em calcular a quantidade de instruções relevantes do algoritmo. O presente trabalho utiliza-se, mais especificamente, do levantamento da quantidade de instruções relevantes do algoritmo para fins de determinação do tempo de execução, introduzindo na ferramenta um mecanismo para realização desta contagem. Para realizar a apresentação desta seção, dividiu-se o conteúdo em duas subseções, a saber, pseudocódigo e contagem de operações primitivas. Na primeira, é apresentado o modo como os algoritmos são representados neste documento. Na segunda, é 23 apresentado um modelo de contagem de instruções primitivas do pseudocódigo para cálculo da complexidade algorítmica. 3.1 Pseudocódigo Pseudocódigo é um código de alto nível escrito em linguagem natural, que permite compreender facilmente a estrutura de um algoritmo. É um misto de linguagem natural com estruturas comuns de programação, permitindo que outras pessoas não habituadas com programação entendam o funcionamento do algoritmo (GOODRICH; TOMASSIA, 2004). Goodrich et Tomassia (2004) estabeleceram um conjunto de notações para as construções de linguagens de programação no pseudocódigo: Declaração do algoritmo: Algoritmo nome (parâmetro 1, parâmetro 2,...) declara um nome para o algoritmo e os seus respectivos parâmetros. Operadores relacionais: o operador de atribuição igual (=) será usado para denotar relação de igualdade em expressões booleanas. Para atribuição, utilizaremos a seta à esquerda (←). Os demais operadores relacionais permanecem inalterados. Operadores lógicos: utiliza-se a linguagem natural desses operadores. Exemplo: e para o operador (&&), ou para o operador (||), e não para operador de negação (~). Operadores aritméticos não utilizam nenhuma notação específica, apenas mantém a notação atual. Laços: faça ações enquanto [condição]; para [atribuição de variável] [condição de saída] passo [incremento] ações; faça enquanto [condição] ações. Usa-se tabulação para delimitar as ações do laço. Indexação: A[i] representa um arranjo de i elementos, que contém posições que variam de 0 até i-1. Estruturas de decisão: se [condição] então ações. Utiliza-se tabulação para amarrar as ações às estruturas de decisão. Dadas às estruturas acima definidas, apresenta-se a figura 1 como um exemplo de pseudocódigo: 24 Figura 1 - Exemplo de Pseudocódigo do Algoritmo Bolha Analisando o pseudocódigo da figura 1, fica fácil entender o seu funcionamento. No primeiro fluxo de execução, quando i e j são iguais à zero, o primeiro elemento do vetor é comparado com o segundo elemento do vetor. Caso a comparação seja verdadeira (linha 3), trocam-se os elementos da primeira e segunda posições do arranjo (linhas 4 a 6). A partir daí, são feitas trocas sucessivas, caso sejam necessárias, até que se atinja o valor de (n-1)2 iterações, quando o vetor já estará ordenado. Após estabelecer normas para representação de algoritmos através do pseudocódigo, resta agora aplicar um modelo de contagem de instruções de código do algoritmo, que será descrito no item seguinte. 3.2 Contagem de Operações Primitivas Esse modelo baseia-se na contagem de operações primitivas do algoritmo. Cada linha do algoritmo é analisada contando-se as operações primitivas, cujo somatório é o tempo de execução do algoritmo T(n). De acordo com GOODRICH et TOMASSIA (2004), as operações abaixo são consideradas operações primitivas: Atribuições; Chamada a métodos; Comparações; Operações aritméticas; 25 Acesso a um vetor; Retornos; Referências a objetos. Tome-se como base o algoritmo da figura 1 para exemplificar o uso desse modelo. Nela se pode observar que na linha 1 existe uma atribuição à variável i antes de entrar no primeiro laço, o que corresponde a uma operação primitiva. Nessa mesma linha, duas operações primitivas são feitas n vezes cada uma, devido à expressão i < n-1: operação aritmética e comparação, contribuindo com 2n operações primitivas. O corpo do código do primeiro laço para executa n-1 vezes. Então, a variável i é incrementada e atribuída, somando 2(n-1) para a contagem. Na linha 2, a variável j recebe o valor zero, cuja atribuição é repetida n-1 vezes. Analisando somente a subtração e a comparação do termo j < n-1, ela executa 2n vezes. Mas como essa comparação pertence ao corpo do primeiro para, então contribui com 2n(n-1) unidades. O corpo do segundo laço para, é executado (n-1)(n-1) vezes. Atribuição e incremento à variável j no final da iteração do segundo laço somam 2(n-1)(n-1) operações primitivas. A linha 3 contém uma operação aritmética, dois acessos a vetores e uma comparação, totalizando 4(n-1)(n-1) operações primitivas. Na linha 4, é realizada indexação ao acessar o arranjo V[j] e atribuição à variável k, somando 2(n-1)(n-1) unidades para contagem. Na linha 5, há uma operação aritmética j+1, acesso a um vetor V[j+1], acesso ao vetor V[j] e atribuição a este de V[j+1], podendo contribuir com 4(n-1)(n-1) unidades. Na linha 6, 3(n-1)(n-1) operações primitivas são possíveis: soma de dois números j+1, acesso ao arranjo V[j+1] e atribuição de k. Assim sendo, o corpo do segundo laço pode ter de 6(n-1)(n-1) a 15(n-1)(n-1) operações primitivas. Portanto, a quantidade de operações primitivas t(n) feitas pelo algoritmo bolha é no mínimo 1+ 2n + 2(n-1) + n-1 +2n(n-1) + 6(n-1)(n-1) = 8n2 - 9n + 4 e no máximo 6n2 - 9n + 4 + 9(n-1)(n-1) = 17n2 - 27n + 13. 26 O melhor caso t(n) = 8n2 - 9n + 4 acontece sempre quando o corpo da linha 3 não é executado, ou seja, quando os elementos do vetor V estão em ordem decrescente. O pior caso ocorre quando os elementos de V estão em ordem crescente. A grande desvantagem na abordagem desse modelo é o grande dispêndio ao separar e contar as operações primitivas num código, visto que existem algoritmos com centenas e até milhares de linhas de código. Na próxima seção será descrito a análise de algoritmos para entradas maiores e as notações comuns de complexidade de algoritmo. 3.3 Análise Assintótica A análise assintótica consiste no estudo do tempo de execução do algoritmo conforme o aumento do tamanho da entrada (CORMEN et al., 2002). Para entradas menores, o custo de execução de um algoritmo é baixo. Porém, quando n aumenta indefinidamente, pode-se mostrar os limites do custo de execução do algoritmo sobre n. A tabela 1, encontrada na obra de Toscani et Veloso (2005), mostra as diferenças de desempenho entre os algoritmos de Crammer e Gauss para resolução de um sistema de equações lineares de tamanho n. Tabela 1 - Tamanho do Problema x Tempo de Execução n 2 3 4 5 10 20 40 Mé todo de Cra mmer 22 µs 102 µs 456 µs 2.35 ms 1.19 mi n 15225 sé culos 5x1033 sé culos Mé todo de Gauss 50 µs 159 µs 353 µs 666 µs 4.95 ms 38.63 ms 0.315 s 27 A tabela 1 denota que, para entradas menores, os métodos de Crammer e Gauss possuem uma diferença mínima no tempo de execução. Entretanto, quando n aumenta consideravelmente, o algoritmo de Crammer vai deixando de ser aceitável na prática. 3.3.1 Notação O Segundo Goodrich et Tomassia (2004), uma função f(n) é considerada O(g(n)) se f(n) e g(n) são funções que englobam números inteiros e existe uma constante real c > 0 e uma constante inteira n0 ≥ 1, de tal sorte que f(n) ≤ cg(n) para todo inteiro n ≥ n0 . A figura 2 ilustra essa definição: Figura 2 - Notação O. Fonte (GOODRICH; TOMAS S IA, 2004) A partir do ponto n0 , a função f(n) nunca ultrapassará a função cg(n). Assim sendo, cg(n) domina assintoticamente f(n), ou seja, g(n) é o limite assintótico superior (CORMEN et al., 2002). Toma-se como exemplo o tempo de execução do pior caso do algoritmo da figura 1. A função f(n) = 17n2 - 27n + 13 é O (n2 ), pois 17n2 - 27n + 13 ≤ 17n2 , cuja constante c = 17 para todo n0 ≥ 1. 28 3.3.2 Notação Ω Ao contrário da notação O, a notação Ω (ômega) limita por baixo a função f(n). Segundo Toscani et Veloso (2005), uma função f(n) é Ω(g(n)), quando f(n) e g(n) são funções que englobam números inteiros e existe uma constante c > 0 e n0 > 0 tal que, f(n) ≥ cg(n). A figura 3 representa bem essa definição: Figura 3 - Notação Ω. Fonte(TOS CANI; VELOS O, 2005) Para exemplificar, toma-se o polinômio do melhor caso do algoritmo bolha, definido na seção 3.2. Para mostrar que f(n) = 8n2 - 9n + 4 é Ω(n2 ), basta fazer c = 7 e n0 ≥ 9 para que a condição (8n2 - 9n + 4) ≥ 7n2 seja verdadeira. No entanto, como essa notação denota que a função f(n) crescerá no mínimo igual à função g(n), é correto afirmar que o melhor caso do algoritmo bolha é Ω(n2 ), pois o segundo laço é executado, mesmo sendo no melhor caso. Assim, tanto a notação O quanto a notação Ω possuem complexidade quadrática. O algoritmo insertion sort citado por CORMEM et al. (2002), por exemplo, demonstra que a complexidade O difere da Ω. O pior caso desse algoritmo é O(n2 ), enquanto que o melhor caso é representado por uma função linear Ω(n), quando o vetor já se encontra ordenado. 29 3.3.3 Notação Θ Conforme Ziviani (2004), f(n) é dito ordem de Θ(g(n)) se existirem três constantes positivas c1 , c2 e n0 tal que, c1 g(n) ≤ f(n) ≤ c2 g(n) para n ≥ n0 . Apresenta-se a figura 4 como exemplo dessa notação: Figura 4 - Notação Θ. Fonte(ZIVIANI, 2004) Na figura acima, a partir de n0 percebe-se que a função f(n) é delimitada por uma região compreendida entre as funções g(n). Assim, g(n) é considerado limite assintótico restrito (CORMEN et al., 2002). A função 5n2 é Θ(n2 ), pois c1 n2 ≤ 5n2 ≤ c2 n2 produz c1 ≤ 5 ≤ c2 . No capítulo seguinte, serão apresentados os requisitos da ferramenta com base nos conceitos de engenharia de software, a arquitetura adotada e as tecnologias utilizadas no desenvolvimento do sistema. 30 4. DESENVOLVIMENTO DA APLICAÇÃO Neste capítulo, serão discutidos os seguintes aspectos relacionados ao desenvolvimento do sistema: requisitos do sistema, arquitetura, ambiente de desenvolvimento e tecnologias empregadas. 4.1 Requisitos do Sistema Os requisitos de um sistema de software denotam uma função ou restrição do sistema e são classificados da seguinte forma (SOMMERVILLE, 2003): Requisitos funcionais: tratam das funções e comportamentos do sistema em determinadas situações. Requisitos não funcionais: definem restrições de qualidade para o sistema em termos de desempenho, confiabilidade, usabilidade, portabilidade, etc. Requisitos de domínio: são requisitos provenientes do domínio de aplicação do sistema e que refletem propriedades deste campo. Podem ser requisitos funcionais ou não funcionais. Nas subseções seguintes, serão apresentados os requisitos da ferramenta implementada neste trabalho, que tem por objetivo calcular a complexidade de um algoritmo passado pelo usuário e mostrar os resultados graficamente com base no cálculo de operações primitivas. 31 4.1.1 Requisitos Funcionais O software deve analisar somente algoritmos escritos em Java. A aplicação deve permitir a utilização de algoritmos recursivos ou iterativos. O cálculo de complexidade será feito a partir da contagem de operações primitivas, conforme descrito na seção 3.2. Os resultados do cálculo de complexidade deverão ser visualizados em um gráfico para facilitar a leitura das informações. 4.1.2 Requisitos não Funcionais Os principais requisitos não funcionais identificados para a ferramenta são apresentados a seguir. Usabilidade a. A interface da aplicação deve ser simples, organizada e legível, permitindo que os usuários sintam-se à vontade para manusear o software. b. A aplicação deve incluir ferramentas para facilitar a edição e visualização de código. c. O sistema deve notificar o usuário sobre quaisquer erros de codificação ou erros grosseiros de utilização. d. Um módulo de ajuda deve estar acessível e legível para o usuário, e deve guiá-lo para uma correta utilização do software. Confiabilidade Em caso de falha de compilação de código ou de qualquer evento anormal, o sistema deve se recuperar. 32 Portabilidade: A aplicação poderá ser executada em qualquer sistema operacional com uma máquina virtual Java instalada. 4.2 Arquitetura Para um melhor entendimento da arquitetura do sistema, será apresentado, primeiramente, um diagrama de atividade que ilustra a lógica principal da aplicação. Figura 5 - Diagrama de atividade da lógica geral da aplicação 33 Na figura 5, percebe-se que inicialmente a aplicação recebe do usuário o código escrito em Java para a geração das entradas que serão computadas por um algoritmo. Em seguida, o sistema processa a entrada, o algoritmo e constrói uma classe contendo uma rotina que alimenta o algoritmo com as entradas. No próximo passo, o sistema compila a classe e, caso não haja erros, executa-a. Os dados resultantes da execução da classe são coletados e impressos em um gráfico. Todo o processo a partir da atividade de processamento da entrada é refeito enquanto existirem entradas a serem computadas. Por fim, quando todas as entradas são processadas, a atividade da aplicação termina e caminha para o estado final. Entendido o funcionamento geral do sistema, a próxima subseção abordará a arquitetura adotada pelo sistema. 4.2.1 Modelagem Arquitetural Na modelagem arquitetural da ferramenta, foi utilizado o MVC (Model View Controller) (ECKSTEIN, on-line), um padrão arquitetural que separa a lógica de negócio da camada de apresentação. O MVC foi introduzido em 1979, sendo inicialmente aplicado para desenvolvimento em Smalltalk. Sua implementação ajudou a desacoplar a lógica de negócio da maneira com que os dados são vistos pelo usuário. A arquitetura MVC divide-se em três camadas: Modelo(Model): Representa a lógica de negócio da aplicação, ou seja, a utilização dos dados da aplicação para agregar funções do domínio do sistema. Visão(View): Representa a camada que interage com o usuário. É através dela que o usuário visualiza ou submete os dados ao sistema. Controlador(Controller): É camada que traduz a interação do usuário com a visão em ações que o modelo irá executar. A figura 6 ilustra a utilização do padrão MVC na modelagem das principais classes do sistema e os seus relacionamentos. 34 Figura 6 - Diagrama de classe da aplicação Fragmentando a figura 6 com base no padrão MVC, percebe-se que o controlador corresponde à classe preenchida com a cor amarela. As visões do sistema são as classes preenchidas com a cor azul. As demais classes ilustradas pela cor verde correspondem ao modelo. A tabela 2 descreve de forma sucinta as classes do sistema. 35 Tabela 2 - Descrição das classes do sistema Classe View LineChartView Controller TamanhoEntrada AnnotationControl Allocator ManipStr Compiler Counter Runner Descrição Tela principal da aplicação. Tela do gráfico resultante da análise do algoritmo. Controlador da aplicação. Traduz as ações do usuário em funções do sistema. Classe que define uma palavra reservada para recepção dos tamanhos e tipo das entradas do algoritmo. Classe que carrega os valores e o tipo das entradas da classe TamanhoEntrada. Classe que aloca tamanhos e valores para um tipo de dado especificado na classe TamanhoEntrada. Classe que prepara uma estrutura de uma classe ConcreteRunner em uma String. Classe que grava a String da classe ConcreteRunner em um arquivo .java, compila e executa. Classe que computa as operações primitivas para cada entrada. Interface que define um método comum a um ConcreteRunner. Na camada da visão, a classe View corresponde à tela principal da aplicação. É através dela, por exemplo, que o usuário insere o código fonte de um algoritmo e solicita ações para análise de complexidade com base no cálculo de operações primitivas. A classe LineChart é responsável pela exibição gráfica dos resultados da análise de complexidade. Na camada de controle, a classe Controller intercepta requisições do usuário vindas da camada de visão e solicita o processamento das requisições às classes pertencentes ao modelo. No modelo, a classe ManipStr é responsável pela recepção do código do usuário e preparação para que o mesmo seja compilado e executado posteriormente na classe ConcreteRunner. 36 A classe TamanhoEntrada define uma anotação para uma palavra reservada do sistema, cuja função é alocar tamanhos e valores para as entradas que serão utilizadas na execução do algoritmo onde as operações serão contadas. Essa anotação atua sobre campos Java e possui uma política de retenção Runtime (ORACLE, on-line). A classe AnnotationControl fornece métodos de controle e de recuperação de dados da anotação. Como os dados só são retidos em tempo de execução utiliza-se a técnica Reflection para recuperação dos dados referentes às entradas. A classe Allocator aloca tamanhos e atribui valores aleatórios para um tipo de dado definido na anotação TamanhoEntrada. Os seguintes tipos são suportados: Primitivos e seus arrays: int, float, long, double, char. Classes Wrappers e seus arrays: Integer, Float, Long, Double, String, Character. A classe Compiler possui funções especiais como compilação e execução da classe construída pelo sistema (ConcreteRunner). Na compilação, utiliza-se a interface JavaCompiler (ORACLE, on-line), para compilar um arquivo a partir de um programa Java. Na execução, carrega-se a classe ConcreteRunner utilizando um ClassLoader próprio e executa esta classe por meio de Reflection. 37 Figura 7 - Diagrama de Sequência da Aplicação A classe ConcreteRunner é sempre alterada com o código passado pelo usuário, compilada e executada. Ela implementa o método run(int x) da interface Runner. Esse método recebe por parâmetro o valor de cada entrada a ser processada pelo conteúdo interno (algoritmo do usuário). A implementação da interface possibilita que a classe ConcreteRunner sempre seja vista como um tipo de Runner pelo sistema, ou seja, o sistema invocará métodos da classe por meio de sua interface. Essa classe, também herda métodos de controle da contagem de operações primitivas da classe Counter, que podem ser empregados em sua estrutura. A herança permite gerenciar a contagem de operações primitivas por meio da superclasse. A figura 7 ilustra uma sequência típica de troca de mensagens entre estas classes. Compreendida a arquitetura e a função de cada componente do sistema, serão apresentadas nas subseções seguintes uma descrição do ambiente de desenvolvimento da aplicação e as principais tecnologias empregadas. 38 4.3 Ambiente de Desenvolvimento Para o desenvolvimento do sistema, foi utilizado o IDE NetBeans 6.9.1 Java SE(Standart Edition) (NETBEANS, on-line). Esta distribuição inclui ferramentas essenciais para programação em Java como: editor, perfil, suporte à refatoração e um módulo de desenvolvimento gráfico. Além disso, a ferramenta de desenvolvimento dispõe de um editor gráfico de componentes da biblioteca swing, permitindo construir aplicações desktop de forma rápida, apenas arrastando e soltando os componentes na tela. 4.4 Tecnologias Empregadas Foi utilizada a linguagem Java tanto para o desenvolvimento da ferramenta quanto para a especificação dos algoritmos que serão analisados através da mesma. Além de ser uma linguagem robusta e bem estabelecida no mercado, para a qual existe uma extensa gama de bibliotecas e frameworks, o Java é adotado na grande maioria dos cursos de computação como padrão para o ensino de técnicas de programação, o que pode suavizar a curva de aprendizado dos usuários durante o uso da ferramenta. Nos próximos subitens serão abordadas as principais tecnologias e bibliotecas da linguagem Java empregadas na implementação da ferramenta. 4.4.1 Reflection Reflection é um recurso que permite que um programa Java examine ou faça introspecção de si mesmo (MCCLUSKEY, on-line). Está presente na linguagem Java desde a versão 1.1 e é através dela que conseguimos visualizar os membros de uma classe, invocar métodos e até instanciar classes dinamicamente. Esta tecnologia é utilizada frequentemente na implementação de funções como code-completion (figura 8), presente nos softwares de desenvolvimento, onde o programador 39 consegue visualizar os membros de uma classe usando o operador ponto (.), após a declaração da classe ou instância da mesma. A figura 8 mostra uma lista de métodos da classe String que podem ser visualizados em tempo de codificação, graças ao Reflection, ilustrando bem um exemplo de utilização. Figura 8 - Exemplo code-completion no Netbeans Este recurso também permite a instanciação de classes e a utilização de seus membros (métodos ou variáveis de instância), apenas declarando o nome da classe. A figura 9 ilustra esta outra situação. Figura 9 - Exemplo de instância de classe e execução de método via Reflection Na primeira linha, foi declarado o nome da classe que se deseja examinar. A classe em questão é empregada para criar estruturas do tipo Hash. Na segunda linha, foi definido o método para execução chamado size que contém um argumento nulo. 40 Na terceira linha, a classe é instanciada e, na última linha, o método é invocado passando um argumento nulo. O emprego de Reflection no projeto permitiu instanciar a classe ConcreteRunner e invocar os seus métodos dinamicamente, além de coletar os valores passados para as anotações. 4.4.2 Annotation Desde a versão 5.0, o Java oferece suporte a uma anotação de proposito geral, a fim de tornar o desenvolvimento mais ágil e eficiente (ORACLE, on-line). As anotações se comportam como metadados, ou seja, dados que descrevem outros dados. Elas não afetam a semântica do programa em execução, mas influenciam como os programas são tratados por ferramentas e bibliotecas. Na definição de uma anotação, acrescenta-se um sinal “arroba” (@) antes da palavra reservada interface seguido de um nome para a anotação. As anotações podem ser definidas nas seguintes estruturas: ElementType.Type – Aplicada apenas ao tipo. Um tipo pode ser uma classe, interface, enum ou até mesmo uma anotação. ElementType.Field – Campos Java. ElementType.Method – Métodos. ElementType.Parameter – Parâmetros dos métodos. ElementType.Constructor – Construtores das classes. ElementType.Package – Pacotes. ElementType.Annotation_Type – Declaração de um tipo de anotação. Uma retention policy (política de retenção) pode ser adotada para definir o domínio de recuperação dos dados da anotação. As políticas de retenção são as seguintes: RetentionPolicy.Source – Este tipo de anotação pode ser retida a nível de código fonte, porém a anotação é ignorada pelo compilador. 41 RetentionPolicy.Class – As informações são retidas em tempo de compilação, mas não estão visíveis pela máquina virtual Java. RetentionPolicy.Runtime - A anotação é retida somente em tempo de execução pela máquina virtual Java. No projeto, uma anotação foi criada sobre uma variável de instância com retenção em tempo de execução, para dar suporte a uma expressão reservada do sistema que será vista mais adiante. 4.4.3 ClassLoader Através da compilação de arquivos de código fonte Java, gera-se arquivos de extensão .class, cujo conteúdo (bytecodes) é processado pela JVM (Java Virtual Machine). A JVM acessa os bytecodes através do ClassLoader (SILVEIRA, on-line). Toda classe Java é carregada por alguma instância de ClassLoader. Esta classe é encontrada no pacote java.lang e serve tanto para o carregamento de classes definidas pelo usuário quanto para o carregamento das classes essenciais da linguagem. A figura 10 ilustra a hierarquia do ClassLoader: Figura 10 - Hierarquia do ClassLoader. Fonte(http://www.developer.com/img/articles/2003/08/14/cl_hierarchy.gif) Na figura 10, os elementos abaixo da hierarquia carregam classes somente se tentativas de carregamento forem delegadas para os elementos acima da hierarquia 42 (TAYLOR, on-line). A JVM possui um carregador de classe em sua implementação chamado Bootstrap ClassLoader, que é responsável pelo carregamento de classes confiáveis da linguagem como (java.*, javax.* ). O Extension ClassLoader carrega as classes do diretório do JRE (java runtime environment). Por último, o System ClassLoader é responsável pelo carregamento de classes do classpath do sistema. A escrita de um próprio ClassLoader no projeto permitiu o carregamento dinâmico da classe que é sempre compilada e executada (ConcreteRunner). É uma classe externa ao sistema, pois não se encontra no diretório padrão dos fontes da aplicação. Caso fosse uma classe interna, existiria a possibilidade de erros de compilação e, consequentemente, a execução da aplicação seria comprometida. Por isso, foi necessário escrever um próprio ClassLoader para esta classe. 4.4.4 JSyntaxPane É um simples editor de código feito em Java. Suporta as seguintes linguagens: Java, JavaScript, Properties, Groovy, C, C++, XML, SQL, Ruby e Python (JSYNTAXPANE, on-line). Possui um conjunto de ferramentas que facilitam a edição de código, além de realçar as palavras reservadas da linguagem ou qualquer expressão. A utilização deste editor contribuiu para melhorar a usabilidade da ferramenta, já que facilita a visualização e a edição de código. Figura 11- JSyntaxPane 43 Nativamente o componente já vem com vários recursos como: recortar, copiar, colar, desfazer, refazer, localizar e substituir, adicionar e remover comentários do código. 4.4.5 JFreeChart O JFreeChart (JFREECHART, on-line) é um software livre que possibilita aos desenvolvedores incluir gráficos de qualidade em suas aplicações. Ele dispõe de uma vasta quantidade de gráficos que podem ser incluídos em componentes swing ou impressos em formato PNG, JPEG, PDF, EPS e SVG. A figura 12 mostra um exemplo de um gráfico desta API. Figura 12 – IlustraçãoJ Linechart (JFreeChart) Um gráfico do tipo LineChart (figura 12) foi adotado no projeto para exibição dos dados referentes à análise do algoritmo. 44 4.4.6 PDFHelp Para criação de uma tela de ajuda para aplicação, utilizou-se o plugin e componente do NetBeans chamado PDFHelp (PDFHELP, on-line), baseado na visualização de arquivos PDF, ilustrado pela figura 13. Figura 13 - Integração do PDFHelp no Netbeans É uma ferramenta de fácil utilização e manutenção. Possui as principais características de um leitor de arquivos PDF, como paginação, pesquisa, zoom, etc. O conteúdo de ajuda é escrito em arquivos PDF e o desenvolvedor apenas define para o componente quais arquivos devem ser listados. O componente incrementa a usabilidade da aplicação, já que fornece mecanismos para buscas de informações e instruções de uso da ferramenta. 45 5. UTILIZAÇÃO DA FERRAMENTA Este capítulo tem por objetivo apresentar o funcionamento da ferramenta. A seção está dividida em três partes: interface, palavras reservadas e exemplos de funcionamento da aplicação. 5.1 Interface A figura 14 ilustra a tela principal da aplicação. Cada componente ou conjunto de componentes estão envolvidos por um retângulo vermelho seguido de uma numeração e serão abordados a seguir: 46 Figura 14 - Tela principal da aplicação 1. Barra de Menu No menu Arquivo existe apenas o submenu Sair, cuja função permite sair do sistema. No menu Help, encontra-se o manual de utilização da aplicação, como visto na figura 15: 47 Figura 15 - Help da aplicação A figura 15 ilustra o módulo de ajuda da aplicação suportado pela API PDFHelp apresentada na seção 4.4.6. 2. Barra de Ferramentas Aqui se encontram as ferramentas que facilitam a edição de código na aplicação. Os botões que merecem destaque são: recortar, copiar, colar, desfazer, refazer, comentar, localizar e substituir código. 3. Editor de Código: É o local onde são escritos o código de geração das entradas e o algoritmo para cálculo de complexidade. Divide-se em duas abas: Geração das Entradas e Contagem de Operações. A aba de geração das entradas permite o desenvolvimento do código de geração das entradas que serão passadas para o algoritmo, que será fornecido na aba de contagem de operações. 48 A aba de contagem de operações pode receber tanto implementações de algoritmos em métodos quanto em classes. Nesta aba, são permitidas importações de classes, mas não são permitidas declarações de pacotes. 4. Contador de Linhas O quarto componente exibe o total de linhas do código e a numeração da linha que se encontra o cursor. 5. Botão Executar A função deste botão é acionar a análise de operações relevantes do algoritmo no editor de código, indicadas pelo usuário, cujos resultados serão exibidos graficamente. 6. Saída de Erros Notifica eventuais erros de compilação de código. 5.2 Palavras Reservadas Neste tópico, serão descritas as palavras reservadas do sistema, que são fundamentais para construção do código para a análise da complexidade. As seguintes palavras reservadas serão destacadas de cor vermelha no editor de código fonte ilustrado nas figuras 16 e 17. @TamanhoEntrada(tam={val1, val2, val3,...})<tipo de dado><nome>; Trata-se de uma anotação que recebe números inteiros positivos (val1, val2, val3,...) como entrada e aloca tamanhos e valores para um tipo de dado definido. Essa expressão facilita a criação das entradas com valores aleatórios para o algoritmo, permitindo que o usuário não tenha o trabalho tedioso de instanciar e atribuir valores para um tipo de dado. Deve ser declarada na aba de geração das entradas no editor de código e sempre deve ser escrita antes da expressão #main, que será vista mais adiante. A figura 16 mostra como deve ser a declaração desta palavra reservada. 49 Figura 16 - Escrita da expressão @TamanhoEntrada Na figura 16, a variável inteira x receberá os valores 1, 2 e 3, respectivamente. Outros tipos de dados são suportados, tais como: a) Primitivos: int, float, long, double, char e seus respectivos arrays. b) Classes Wrapper: Integer, Float, Long, Double, Character, String e seus respectivos arrays. #main É uma expressão da aplicação que separa a declaração da palavra reservada @TamanhoEntrada da chamada do algoritmo que se encontra na aba de contagem de operações. Deve ser declarada sempre após a definição da anotação @TamanhoEntrada. @contar(int x) Serve para computar as operações relevantes de um algoritmo. Recebe um parâmetro inteiro opcional que corresponde ao número de vezes que uma operação é incrementada. Deve ser declarada no escopo da operação primitiva que se deseja computar na aba de contagem de operações. 50 Figura 17 - Escrita da expressão @contar 5.3 Exemplos Nesta seção, serão abordados três exemplos de algoritmos para ilustrar o emprego das palavras reservadas e o funcionamento do cálculo de operações relevantes no software. 5.3.1 Exemplo 1 O primeiro exemplo consiste de um algoritmo contendo apenas uma estrutura do tipo laço. Todas as operações primitivas serão computadas para fazer um comparativo com a teoria de operações relevantes vista na seção 3.2. Figura 18 - Geração das entradas Exemplo 1 Na figura 18, foram definidas as entradas de tamanho 1, 2 e 3 utilizando a palavra reservada @TamanhoEntrada que serão armazenadas pela variável x (linha 2). Na linha 4, 51 definiu-se a expressão #main para separar o código de geração das entradas da chamada do método contagem(x). Esse método é chamado três vezes, passando por parâmetros os valores 1, 2 e 3, respectivamente. A estrutura do método é encontrada na aba Contagem das Operações, ilustrado pela figura 19: Figura 19 - Contagem das operações Exemplo 1 No método da figura 19, encontramos uma estrutura do tipo laço delimitada pelas linhas 4 e 11. Na linha 4, antes do primeiro laço ser computado, uma operação primitiva é feita devido à atribuição i = 0. No escopo do laço, são realizadas n operações primitivas, correspondente a expressão i<tam e 2n operações são feitas com base na atribuição e incremento da variável i. Uma última operação relevante é computada quando a expressão i<tam for falsa. Assim, no total temos 1 + n + 2n + 1 = 3n + 2 operações primitivas, onde n corresponde à entrada do algoritmo. As linhas 3, 6, 8, 10 e 13 retratam pontos de contagem de operações relevantes indicados pelo usuário através da palavra reservada @contar. Neste caso, os pontos de contagem refletem a complexidade de operações relevantes 3n + 2, calculado anteriormente. Ao executar o processamento do algoritmo, obtém-se o seguinte gráfico: 52 Figura 20 - Gráfico Exemplo 1 A figura 20 mostra a relação entre a entrada passada pelo usuário e o cálculo de operações primitivas do código. Aplicando o valor da primeira entrada na equação de complexidade 3n + 2, obtém-se o valor 3(1) + 2 = 5, que é valor o correspondente à quantidade de operações primitivas na ordenada. Na segunda entrada, temos 3(2) + 2 = 8 operações relevantes. Por último, na terceira entrada, temos 3(3) + 2 = 11. Assim sendo, prova-se que a aplicação está contando todas as operações primitivas para o exemplo em questão. 5.3.2 Exemplo 2 Neste exemplo, será abordado como é feita a definição e instanciação de classes Java na aplicação. Apresenta-se, primeiramente, o código da contagem das operações na figura 21. 53 Figura 21 - Contagem das operações Exemplo 2 O código da figura 21 tem a função de percorrer um array de Integer e adicionar os números pares em uma lista. O software suporta a importações de classes comuns da linguagem, como também classes do usuário no classpath do Java. Uma peculiaridade vista na linha 4 é que a classe Numero possui um modificador de acesso padrão. Esta classe será declarada internamente na estrutura da classe ConcreteRunner e pode ter também o modificador de acesso public. A linha 8 ilustra à contagem de duas operações primitivas, passadas por parâmetro para a expressão @contar(). Esta expressão computa as operações resto da divisão e comparação, vistas na linha 9. A próxima figura mostra o código de geração das entradas. Figura 22 - Geração das entradas Exemplo 2 54 Nas linhas 2 e 3 encontram-se a notação para definição das entradas para um array de Integer. Em seguida, na linha 6, uma instanciação comum à linguagem Java. Por fim, chama-se o método nPar pela instancia de Numero passando arrays de Integer, cujos tamanhos são 7, 10, 14, 19, 56, 98, 103, 500, 1000, respectivamente. A figura 23 mostra o gráfico da computação das operações relevantes do código. Figura 23 - Gráfico Exemplo 2 4.3.3 Exemplo 3 O software não se limita a apenas algoritmos simples. Algoritmos mais robustos como quicksort podem ser testados para verificar o comportamento da aplicação. A figura 24 ilustra a implementação do quicksort: 55 Figura 24 - Contagem das operações Exemplo 3 Na figura 24, percebe-se que conta-se a quantidade de comparações do primeiro laço, linha 18. A linha 17 computa a última comparação do laço, quando i <= j for falso. A linha 19 conta quantas vezes a expressão i <= j é verdadeira. Com base nas entradas da figura 25, obtém-se o gráfico ilustrado pela figura 26. 56 Figura 25 - Geração das entradas Exemplo 3 Figura 26 - Gráfico Exemplo 3 57 O Anexo A – Algoritmo Complementar dispõe de um algoritmo de busca em árvore binária, exemplificando o funcionamento da ferramenta em algoritmos mais complexos. O algoritmo da torre de hanoi, cuja complexidade é exponencial foi executado sobre entradas grandes, a fim de verificar o funcionamento da aplicação sob stress. O software obteve um desempenho aceitável e nenhum evento anormal foi observado. 58 6. AVALIAÇÃO DA FERRAMENTA NO CONTEXTO EDUCACIONAL Neste capítulo, serão apresentados os resultados de uma pesquisa realizada para verificar a aceitação da ferramenta pelos usuários. Divide-se em duas seções, a saber: questionário e resultados. 6.1 Questionário Foi elaborado um questionário com 10 perguntas como um meio de se obter um feedback dos usuários. As perguntas buscam avaliar alguns critérios de software educacional vistos na seção 2.1. Na tabela 3, se encontram as perguntas e as relações com os critérios de software educacional. 59 Tabela 3 - Perguntas x Critérios Pergunta 1) O sofware possui um design agradável e de fácil leitura? 2) É fácil encontrar e manusear os componentes da tela? 3) Consegue interpretar bem as informações na representação gráfica do algoritmo? 4) Sente-se confuso com a quantidade de informações na tela? 5) O editor de código facilita na codificação? 6) As expressões reservadas do sofware possuem um bom manuseio? 7) A tela de ajuda é clara e de boa leitura? 8) Localiza facilmente informações sobre utilização do software? 9) O sistema reage bem a erros grosseiros de utilização? 10) O software atende bem ao seu propósito? 6.2 Critérios Interface: atrativo para grupos de usuários; boa apresentação. Qualidade: utilização simples de suas funções essenciais. Aprendizagem: fácil leitura. Aprendizagem: informações reduzidas na tela. Qualidade: utilização simples das funções essenciais. Qualidade: utilização simples das funções essenciais. Qualidade: instruções sobre uso(help). Aprendizagem: fácil leitura. Interface: atrativo para grupos de usuários; intuitivo. Qualidade: reação a erros de utilização. Questão opinativa. Resultados O software desta obra foi apresentado brevemente para uma turma da disciplina de Paradigmas de Linguagens de Programação do curso de Ciência da Computação da Faculdade Farias Brito, composta por 13 alunos, os quais já tinham conhecimentos sobre complexidade de algoritmos. Os alunos receberam instruções sobre o funcionamento da ferramenta através de exemplos práticos de contagem de operações de algoritmos. Em seguida, os alunos manipularam a aplicação e responderam um questionário com a finalidade de se avaliar aspectos qualitativos da ferramenta e sua aceitação. Vale ressaltar que a pesquisa foi feita junto a uma quantidade limitada de alunos e os resultados obtidos não foram submetidos a um tratamento estatístico rigoroso. Portanto, estes devem ser encarados apenas como um indicativo da adequação da ferramenta no que concerne aos critérios adotados. A tabela 4 mostra os resultados da avaliação: 60 Tabela 4 - Resultado da avaliação do software 1) O software possui um design agradável e de fácil leitura? 2) É fácil encontrar e manusear os componentes da tela? 3) Consegue interpretar bem as informações na representação gráfica do algoritmo? 4) Sente-se confuso com a quantidade de informações na tela? 5) O editor de código facilita na codificação? 6) As expressões reservadas do software possuem um bom manuseio? 7) A tela de ajuda é clara e de boa leitura? 8) Localiza facilmente informações sobre utilização do software? 9) O sistema reage bem a erros grosseiros de utilização? 10) O software atende bem ao seu propósito? Concordo Fortemente Concordo Indeciso Discordo Discordo Fortemente 54% 38% 8% 23% 62% 15% 46% 46% 8% 23% 16% 46% 15% 23% 61% 8% 8% 8% 46% 46% 46% 46% 8% 7% 77% 8% 8% 54% 31% 15% 54% 15% 31% Na primeira questão, a maioria dos alunos sentiu-se atraída pela interface principal da aplicação e conseguiu ler facilmente os componentes da tela. Apenas 8% dos usuários tiveram dúvidas sobre este quesito. Na segunda questão, 62% dos alunos concordaram com a facilidade de encontrar e manusear funções essenciais da aplicação. Na terceira questão, 92% dos usuários pelo menos 61 concordam com a facilidade de interpretação das informações na representação gráfica da análise do algoritmo. Na quarta questão, 46% dos alunos discordam que a quantidade de informações na tela prejudica a leitura. Na quinta questão, a maioria aprovou o componente de edição de código da aplicação. Na sexta questão, 54% dos usuários pelo menos concordam que as palavras reservadas do sistema possuem um bom manuseio. Os outros 46% ficaram indecisos, o que não deixa de ser um resultado ruim neste quesito, já que leva-se um certo tempo para familiarização com as expressões reservadas da aplicação. Na sétima questão, 92% dos alunos concordam e concordam fortemente que o módulo de ajuda da aplicação é claro e legível. Na questão seguinte 77% dos usuários concordam com a facilidade busca de informações da tela de ajuda. A nona questão denota que 54% dos alunos concordam que a aplicação reage bem a erros grosseiros de utilização. Por último, na décima questão, os usuários aprovaram a ferramenta com 85% de aceitação, demonstrando que a mesma pode servir como um instrumento agregador no ensino de complexidade algorítmica. 62 7. CONCLUSÃO O estudo de complexidade de algoritmos envolve conceitos não triviais e carece de uma maior atenção e dedicação do aluno. Softwares educacionais podem auxiliar no aprendizado, possibilitando o experimento de conceitos através de exemplos práticos. Este trabalho teve por objetivo desenvolver uma aplicação que calcule a complexidade de um algoritmo escrito em Java e exibir a curva de complexidade, servindo de auxílio no ensino de complexidade algorítmica. O trabalho levantou a questão da inserção da informática na educação, onde o computador é programado para ensinar e ser ensinado. Critérios de softwares educacionais, como qualidade, interface e resultados de aprendizagem foram apresentados como características intrínsecas de um bom software educacional. Foram abordados os principais conceitos de complexidade de algoritmos e introduzidas às notações que exprimem limites de crescimento dos custos dos algoritmos. Uma metodologia para análise de algoritmos foi definida, baseada na contagem de operações primitivas. O processo de desenvolvimento do software, guiado pelo modelo de contagem de operações primitivas e pelos critérios de softwares educacionais, envolveu a definição de requisitos, arquitetura, ambiente e tecnologias empregadas no desenvolvimento. Vários exemplos de utilização foram criados para ilustrar o funcionamento da ferramenta e realizar uma avaliação de sua interface visual. 63 A ferramenta foi apresentada para uma pequena turma do curso de Ciência da Computação da Faculdade Farias Brito. Os alunos manusearam a aplicação e avaliaram a ferramenta através de um questionário que envolve critérios de software educativo. Com base nos resultados da avaliação, percebeu-se que os alunos tiveram uma boa impressão da ferramenta, mostrando que ela tem potencial de ser empregada como ferramenta pedagógica. Como trabalho futuro, pode-se buscar a implementação de um mecanismo de análise automática de algoritmos, ou seja, o próprio sistema detecta e conta as operações primitivas. Assim, livra-se o usuário do trabalho tedioso de separar e contar as operações relevantes do código. Outra sugestão seria a possibilidade do usuário definir uma equação que representa uma função de complexidade que pudesse ser visualizada no gráfico conjuntamente com a curva gerada através da contagem, permitindo ao usuário comparar o gráfico da contagem de operações primitivas com um gráfico de crescimento assintótico, por exemplo. O presente trabalho contribuiu como mais um software que se preocupa em utilizar conceitos pedagógicos, além de fomentar o desenvolvimento de aplicativos educacionais para auxiliar ou complementar no estudo de disciplinas que exigem mais dedicação. 64 REFERERÊNCIAS BIBLIOGRÁFICAS BORGES, M.A.F. Aprendizagem e avaliação em um curso à distância. Colabora - Revista Digital da CVA-RICESU, v.1, n. 3, fevereiro 2002. 8 pags. CORMEN, Thomas H.; LEISERSON, Charles E.; RIVEST, Ronald L; STEIN, Clifford. Algoritmos: teoria e prática. Tradução da segunda edição americana por Vandenberg D. de Souza. Rio de Janeiro: Elsevier, 2002. ECKSTEIN, Robert. Java SE Aapplication design with MVC. <http://www.oracle.com/technetwork/articles/javase/mvc-136693.html>. Disponível em Acesso em 05/05/2011. GOODRICH, Michael T; TOMASIA, Roberto. Projeto de algoritmos: fundamentos, análise e exemplos de internet. Tradução: Bernardo Copstein e João Batista Oliveira. Porto Alegre: Bookman, 2004. JFREECHART. JFreeChart. Disponível em <http://www.jfree.org/jfreechart/index.html>. Acesso em 10/06/2011. JSYNTAXPANE. Jsyntaxpane: project home. Disponível em < http://code.google.com/p/jsyntaxpane/>. Acesso em 10/06/2011. LUCENA, Marisa. Diretrizes para a capacitação do professor na área de tecnologia educacional: critérios para a avaliação de software educacional. Disponível em < http://www.inf.pucrs.br/~marciabc/20072/infoesp/apoio/formacaoprofs_avaliacaoSW.pdf >. Acesso em 09/03/2011. MCCLUSKEY, Glen. Using java reflection. Disponível em <http://java.sun.com/developer/technicalArticles/ALT/Reflection>. Acesso em 18/04/2011. NETBEANS. Download NetBeans IDE 6.9.1. http://netbeans.org/downloads/6.9.1/>. Acesso em 10/06/2011. Disponível em < 65 ORACLE. Annotations. Disponível em <http://download.oracle.com/javase/1.5.0/docs/guide/language/annotations.html>. Acesso em 27/04/2011. ORACLE. Enum ElementType. Disponível em < http://download.oracle.com/javase/1,5.0/docs/api/java/lang/annotation/ElementType.html>. Acesso em 29/05/2011. ORACLE. Enum RetentionPolicy. Disponível em <http://download.oracle.com/javase/1,5.0/docs/api/java/lang/annotation/RetentionPolicy.html >. Acesso em 29/05/2011. ORACLE. Inteface JavaCompiler. Disponível http://download.oracle.com/javase/6/docs/api/javax/tools/JavaCompiler.html>. em < Acesso em 29/05/2011. PDFHELP. PDFhelp: superior JavaHelp replacement for all Java developers. Disponível em < http://www.pdfhelp.org/index.php>. Acesso em 10/06/2011. PRESSMAN, Roger S. Engenharia de software. 6. ed. São Paulo: McGraw-Hill, 2006. SANTANCHÉ, C.A.; TEIXEIRA, C. Integrando instrucionismo e construcionismo em aplicações educacionais através do casa mágica. Salvador. <http://www.nuppead.unifacs.br/artigos/IntegrandoInstrucionismo.pdf>. Disponível em Acesso em 09/03/2011. SANTOS, R. P.; COSTA, H. A. X. TBC-AED e TBC-AED/WEB: Um Desafio no Ensino de Algoritmos, Estruturas de Dados e Programação. In: IV Workshop em Educação em Computação e Informática do Estado de Minas Gerais (WEIMIG’2005), Varginha/MG – Brasil. CD dos Anais do IV Workshop em Educação em Computação e Informática do Estado de Minas Gerais, 2005. v. 1. SILVEIRA, Tiago. Classloaders. Disponível em <http://www.guj.com.br/articles/124>. Acesso em 28/04/2011. SOMMERVILLE, Ian. Engenharia de Software. Tradução André Mauricio de Andrade Ribeiro. São Paulo – SP; Pearson Addison Wesley – 2003. 66 TAYLOR, Brandon E. Java class loading: the basics. Disponível em <http://www.developer.com/java/other/article.php/10936_2248831_2/Java-Class-LoadingThe-Basics.htm>. Acesso em 28/04/2011. TOSCANI, Laira Vieira; VELOSO, Paulo A. S. Complexidade de algoritmos. Porto Alegre: Instituto de informática da UFRGS: Editora Sagra Luzzatto, 2005. VALENTE, José A. Formação de profissionais na área de informática em educação. Disponível em: http://www.iei.org.br/~vanderlei/anteriores/2007/tecnico/teorias_aprendizagem/valente.pdf < > Acesso em 09/03/2011. VALENTE, José A. Informática na Eeducação: Instrucionismo x Construcionismo. Campinas: Núcleo de Informática Aplicada à Educação - Nied - Universidade Estadual de Campinas, 1997. Manuscrito não publicado. ZIVIANI, Nilvio. Projeto de algoritmos: com implementações em Pascal e C. 2. ed. rev. e ampl. São Paulo: Pioneira Thomson Learning, 2004. 67 ANEXO A – ALGORITMO COMPLEMENTAR Árvore Binária i. Código da aba de geração das entradas. @TamanhoEntrada(tam={4,8,16,56,78,89,120,149}) int x; #main BinaryTree b = new BinaryTree(); b.gerarArvore(x); b.pesquisar(b, 18); ii. Código de contagem das operações. import java.util.ArrayList; import java.util.Collections; import java.util.Random; /** * * @author rodson */ class BinaryTree { public BinaryTree noEsq = null; public BinaryTree noDir = null; public int dado = -1; public int getRandomNumber(int inicio, int fim) { Random random = new Random(); long range = (long) fim - (long) inicio + 1; long fraction = (long) (range * random.nextDouble()); int randomNumber = (int) (fraction + inicio); return randomNumber; } public void gerarArvore(int qtdNo){ ArrayList<Integer> arr = new ArrayList<Integer>(); for(int i=0; i<qtdNo; i++){ arr.add(i); } int k = arr.get(arr.size()/2); inserir(this, k); for(Integer i : arr){ if(i!=k){ inserir(this, i); } } 68 } public void inserir(BinaryTree no, int dado) { if (no.dado == -1) { no.dado = dado; } else { if (no.dado > dado) { if (no.noEsq == null) { BinaryTree b = new BinaryTree(); b.dado = dado; no.noEsq = b; } else { inserir(no.noEsq, dado); } } else if (no.dado < dado) { if (no.noDir == null) { BinaryTree b = new BinaryTree(); b.dado = dado; no.noDir = b; } else { inserir(no.noDir, dado); } } } } public void preOrdemAjudante(BinaryTree no) { if (no == null) { return; } preOrdemAjudante(no.noEsq); preOrdemAjudante(no.noDir); System.out.println(no.dado + ""); } public boolean pesquisar(BinaryTree no, int key){ if(no == null){ //contando retorno @contar(); return false; } else { if(no.dado < key){ //contando retorno @contar(); return pesquisar(no.noDir, key); } else{ if(no.dado > key){ //contando retorno @contar(); return pesquisar(no.noEsq, key); } else{ //contando retorno @contar(); return true; } 69 } } } }