Upload
phamque
View
235
Download
0
Embed Size (px)
Citation preview
Aulas de
Algoritmos e Modelacao Matematica
Paulo Mateus
Andre Souto
2013
A guardia
ii
Precaucao dirigida aos alunos
Os temas apresentados neste trabalho sao uma versao muito preliminar da sebenta da cadeira de Al-
goritmos e Modelacao Matematica dos cursos de Engenharia Biomedica e de Matematica Aplicada a
Computacao do Instituto Superior Tecnico da Universidade de Lisboa. Notem que pode haver gralhas,
exemplos incompletos, definicoes e teoremas que podem estar apresentados de forma nao uniforme.
Na verdade podem existir topicos que necessitarao de ser reescritos ou escritos de raiz. Parte dos
exercıcios sao retirados das referencias bibliograficas dadas. Futuramente, serao substituıdos por outros
que possam servir os nossos propositos enquanto docentes da cadeira e de acordo com os objetivos
estabelecidos para os conhecimentos a adquirir pelos alunos.
No que diz respeito a esta notas o leitor deve estar preparado para se deparar com problemas de
compreensao das materias abordadas, dado que serao para ja muito incompletas, e portanto sugerimos
a leitura de livros considerados importantes nas areas que serao abordadas de modo a complementarem
a informacao apresentada. Sugerimos por exemplo a leitura de [CSRL01], [Eck02] e outras referencias
que faremos ao longo deste trabalho.
iii
Prefacio
Este trabalho tenciona compilar num livro os conteudos programaticos de um curso introdutorio a
Algoritmia e a Modelacao Matematica. Os temas abordados neste trabalho sao naturalmente parte inte-
grante dos conteudos programaticos planeados para a cadeira de Algoritmos e Modelacao Matematica
a lecionar nos cursos de Engenharia Biomedica e de Matematica Aplicada a Computacao do Instituto
Superior Tecnico da Universidade de Lisboa. A primeira compilacao data do ano de 2013.
A ideia deste trabalho e providenciar ao alunos (e nao so) uma referencia de estudo, ainda que incom-
pleta, sobre algoritmia com uma forte componente matematica de analise e correcao sendo por isso
dado um especial enfase a estas problematica sob o ponto de vista de matematico e do ponto de vista
das ciencias da computacao. A juntar a este objetivo pretende-se que este compendio possa futuramente
ser tambem uma ferramenta util para outros cursos que abordem tematicas semelhantes.
Pelo apoio financeiro, o autor Andre Souto esta profundamente grato a Fundacao para a Ciencia e
Tecnologia pela bolsa SFRH/BPD/76231/2011.
Estamos tambem gratos a todos os membros do SQIG pelo excelente ambiente de trabalho pelo enco-
rajamento dado. Ficamos tambem gratos aos alunos por qualquer comentario que ajudem a melhorar
este trabalho.
2012.
Departamento de Matematica
Instituto Superior Tecnico, Universidade de Lisboa
Security and Quantum Information Group
v
Instituto de Telecomunicacoes
Conteudo
Precaucao dirigida aos alunos iii
Prefacio v
Conteudo vi
I Aulas Teoricas 3
1 Introducao e Bibliografia 5
1.1 Metodo de Avaliacao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2 Bibliografia recomendada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3 Conteudos programaticos da cadeira . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.4 Ferramentas a utilizar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.5 Java - Um primeiro contacto e um exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2 Pacotes utilitarios em Java 11
2.1 Introducao ao Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.2 Funcoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.2.1 Efeitos colaterais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.3 Tipos de dados primitivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.4 Tipos e expressoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.5 Composicao alternativa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.6 Atribuicoes Quick and Dirty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.7 Recursao em Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.8 Declaracao de vetores e matrizes e tensores . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.9 Notacao Assimptotica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
vi
2.10 Custos das operacoes em Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3 Analise da correcao e complexidade de algoritmos imperativos: ordenacao 27
3.1 Nocao de invariante e variante de um ciclo . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.2 Algoritmos de ordenacao e complexidade no pior caso e caso medio . . . . . . . . . . . . 31
3.2.1 Algoritmo de Insercao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.2.2 Algoritmo Bubblesort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4 Analise de algoritmos recursivos: Quicksort 37
4.1 Continuacao dos algoritmos de ordenacao . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
4.1.1 Algoritmo QuickSort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
4.2 Arvores de recursao e o Teorema Mestre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.3 Analise do algoritmo de Quicksort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.3.1 O melhor caso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.3.2 O pior caso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.3.3 O caso medio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
5 Tipos de dados abstratos e interfaces em Java 49
5.1 Tipos de dados abstratos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
5.2 Implementacao de tipos de dados abstratos em Java . . . . . . . . . . . . . . . . . . . . . . 52
5.2.1 Implementacao estatica de Listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
5.2.2 Implementacao dinamica de Listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
6 Nocoes de Heranca e Polimorfismo 59
6.1 Heranca . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
6.2 Polimorfismo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
7 Pesquisa em Tabelas 65
7.1 Pesquisa em Tabelas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
7.2 Implementacao em listas ligadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
7.3 Tabelas de dispersao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
7.4 Funcao geradora . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
7.4.1 Caso de insucesso da pesquisa em tabelas . . . . . . . . . . . . . . . . . . . . . . . . 72
7.4.2 Caso de sucesso da pesquisa em tabelas . . . . . . . . . . . . . . . . . . . . . . . . . 73
7.5 Escolha da funcao de dispersao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
vii
8 Arvores binarias de pesquisa 77
8.1 Arvores binarias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
8.2 Arvores binarias de pesquisa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
9 B-trees 87
9.1 Complexidade da insercao aleatoria em arvores binarias de pesquisa . . . . . . . . . . . . 88
9.2 B - trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
10 Algoritmos em grafos 97
10.1 Grafos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
10.2 Problema da acessibilidade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
10.3 Busca em Largura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
10.4 Arvore de Extensao Mınima . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
10.4.1 Grafos planares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
11 Leitura e escrita em ficheiro usando Java 111
11.1 Serializacao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
11.2 Escrita e leitura de ficheiros em java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
II Projeto 117
1.1 Objetivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
1.2 Conceitos basicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
1.2.1 Classificador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
1.2.2 Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
1.2.3 Classificar vs estimar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
1.2.4 Redes de Bayes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
1.2.5 Aprendizagem de Redes de Bayes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
1.2.6 Minimum Description Length (MDL) . . . . . . . . . . . . . . . . . . . . . . . . . . 123
1.3 Tipos de dados para a 1a entrega . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
1.3.1 Amostra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
1.3.2 Grafos orientados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
1.3.3 Redes Bayesianas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
Bibliografia 129
1
Parte I
Aulas Teoricas
3
AULA
1Introducao e Bibliografia
• Avaliacao;
• Bibliografia;
• Conteudos programaticos da cadeira;
• Ferramentas a utilizar;
• Um primeiro exemplo de um programa em Java;
5
1.1 Metodo de Avaliacao
SECCAO 1.1
Metodo de Avaliacao
Avaliacao da cadeira sera feita segundo os seguintes parametros:
1 exame - a contar 50% da nota final, cujas datas devem ser defindas pelos alunos
1 projeto - a contar os restantes 50% da nota final, cuja elaboracao sera feita em Java. A descricao
do projecto sera publicada por volta da Semana 4 e tera tres fases de entrega/apreciacao:
1a Parte - a entregar na Semana 8;
2a Parte - a entregar na Semana 13;
3a Parte - uma oral a realizacao na semana 14 onde sera inferido a involvencia individual
de cada aluno na realizacao do projeto.
Bonificacao - a aplicar a quem so entregar um dos exames previstos.
SECCAO 1.2
Bibliografia recomendada
Para a cadeira, entre outras que serao oportunamente acrescentadas, sugerem-se como referencias bibli-
ograficas principais:
[CSRL01] T. Cormen, C. Stein, R. Rivest, and C. Leiserson. Introduction to Algorithms. McGraw-
Hill Higher Education, 2nd edition, 2001.
[Eck02] B. Eckel. Thinking in Java. Prentice Hall Professional Technical Reference, 3rd edition,
2002.
[MS13] P. Mateus, A. Souto, Aulas de Algoritmos e Modelacao Matematica, Preprint 2013 (em
preparacao)
6
Capıtulo 1. Introducao e Bibliografia
SECCAO 1.3
Conteudos programaticos da cadeira
Do programa da disciplina fazem parte os seguintes conteudos:
1. Programacao imperativa em JAVA. Introducao ao estudo da correcao e eficiencia de algoritmo.
Invariantes, analise no pior caso e no caso medio. Mecanismos de modularidade e orientacao por
objectos: heranca, polimorfismo. Programacao de GUI. Utilizacao de API’s.
2. Algoritmos de ordenacao elementares e avancados: insercao direta, selecao direta, bubblesort,
quicksort, fusao binaria e heapsort. Algoritmos e tipos de dados abstratos: pilhas, filas de espera,
filas de prioridade, arvores, acervos. Implementacoes vetoriais e dinamicas. Arvores binarias de
pesquisa. Arvores de pesquisa equilibradas. Tabelas de dispersao. Resolucao de colisoes por
encadeamento e por enderecamento aberto. Enderecamento linear, quadratico e dispersao dupla.
Grafos e grafos pesados;
3. Complexidade computacional: P, NP, problema NP completo, reducao polinomial. Tese Churck-
Markov- Turing para circuitos Booleanos. Teorema de Cook e SAT.
Chama-se a atencao dos alunos para o facto de os conteudos poderem por vezes nao ser apresentados
pela ordem aqui expressa.
SECCAO 1.4
Ferramentas a utilizar
Para um bom funcionamento da cadeira sera necessario que os alunos obtenham as seguintes ferramen-
tas:
• Eclipse IDE for Java Developers disponıvel em
http://www.eclipse.org/downloads/packages/eclipse-ide-java-developers/junosr1
7
1.5 Java - Um primeiro contacto e um exemplo
• Java JDK no Windows, Mac OS, Linux disponıvel em
http://www.oracle.com/technetwork/java/javase/downloads/index.html
SECCAO 1.5
Java - Um primeiro contacto e um exemplo
A tıtulo de exemplo apresenta-se de seguida um programa em linguagem Java. O programa pretende
que seja transmitido ao utilizador, quando corrido, a mensagem “Ola Mundo!” em Ingles, ou seja,
“Hello World!”.
1 public class myfirstjavaprog
2 public static void main(String args [])
3 System.out.println("Hello World!");
4
5
Cada linguagem de programacao tem as suas especificacoes no que diz respeito a sintaxe de escrita! A
linguagem Java esta pensada para uma utilizacao multiplataforma e e orientada a objetos pois, na sua
base de escrita de programas estao, como o proprio nome indica, os objetos que, como se vera mais
a frente tem, entre outras, atributos. No caso apresentado, o comando System.out.println instrui o
sistema a produzir, numa linha, o conteudo que lhe e dado. A versatilidade de um programa em Java
decorre do facto de deste ser compilado, em vez do sistema suporte da maquina onde corre, numa
maquina virtual inerente ao proprio desenvolvimento de Java. Esta funcionalidade traz tambem a
vantagem de a linguagem Java pode ser mais rapida do que se fosse simplesmente interpretada. O
codigo gerado na compilacao pode ser transportado entre plataformas distintas que suporte tecnologia
Java (nomeadamente Windows, Mac/Os, Unix, etc..) nao sendo, portanto, necessario recompilar um
programa antes de o executar, ao contrario do que acontece por exemplo com programas escritos em
linguagem C.
Na figura seguinte esquematiza-se de forma sucinta a execucao de um programa em Java.
8
Capıtulo 1. Introducao e Bibliografia
Figura 1.1: Esquema da execucao de um programa em Java.
A tecnologia Java e distribuıda para 3 plataformas:
1. J2EE (Entreprise Edition), para desenvolvimento de aplicacoes empresariais.
2. J2ME (Micro Edition), para dispositivos de capacidades limitadas (telemoveis e PDA’s).
3. J2SE (Standard Edition), para desktop e servidores.
Debaixo da plataforma existem varios directorios:
1. J2xx Runtime Environment (JRE):
a) Interpretador JVM, classes de ambiente, . . .
b) Usado apenas para correr aplicacoes.
2. J2xx Development kit (JDK):
9
1.5 Java - Um primeiro contacto e um exemplo
a) JRE, compilador, classes utilitarias (Swing, ...), ...
b) Usado no desenvolvimento de aplicacoes.
10
AULA
2Pacotes utilitarios em Java
• Introducao ao Java;
– Edicao e compilacao de programas;
– Tipos e expressoes
– Declaracao de variaveis
– Atribuicao, composicao sequencial, iterativa e alternativa de comandos
– Definicao de funcoes e recursao
– Efeitos colaterais
– Vetores e matrizes
– Complexidade das operacoes em C e notacao assimptotica
– Analise de complexidade do insertsort
11
2.1 Introducao ao Java
SECCAO 2.1
Introducao ao Java
Como ja foi referido, ao contrario do Mathematica e de outras linguagens de programacao, o codigo
Java e compilado para uma maquina virtual em vez de interpretado. O compilador gera um ficheiro
class em codigo maquina que pode ser chamado do sistema operativo com a linha de comando java. O
codigo e editado, compilado e depurado no ambiente Eclipse.
No que se segue pretende-se introduzir os conceitos basicos da linguagem Java, nao na sua potencia-
lidade maxima, isto e, lidando diretamente com objetos, mas a forma de programar sequencial usual
em linguagens de programacao como C. Ao tipo de programas que nao usam as potencialidades da
utilizacao de objetos costuma dar-se o nome de “pacotes utilitarios”.
Considere-se o seguinte exemplo de um programa em Java que calcula o fatorial de 10.
Exemplo 2.1 (Calculo de fatorial de 10)
1 class Utils
2 // Exemplo 1: factorial de 10
3 public static void main(String args [])
4 int i,r;
5 r=1;
6 i=2;
7 while(i<=10)
8 r=r*i;
9 i++;
10
11 System.out.println("A solucao e:"+r);
12
13
Neste exemplo e de salientar os seguintes factos:
• a estrutura basica comeca com uma classe que basicamente designa o conjunto de programas
do qual faz parte o “main” e outras funcoes que visam a elaboracao de uma tarefa, no caso o
12
Capıtulo 2. Pacotes utilitarios em Java
calculo do fatorial de 10.
• para inserir qualquer comentario numa linha de codigo acrescenta-se uma barra inclinada
para a direita dupla, //, que o proprio programa interpreta como sendo algo que nao e para
interpretar ou compilar.
• o tipo de dados retornado pelo “main” e void isto e vazio, pois nao se espera que o main
devolva algum resultado mas sim que execute as tarefas descritas no seu interior.
• a declaracao public static sera estudada pormenorizadamente mais adiante
• a estrutura main(String args[ ]) sera analisada mais a frente e neste momento o leitor deve
pensar nela como algo que e fixo.
• qualquer variavel que se utilize tem de ser declarada. Ao contrario de outras linguagens
a declaracao pode ser feita em qualquer altura do programa, nao sendo necessario alocar a
memoria dessa variavel a priori, isto e, assim que o programa se inicia. Cada variavel tem uma
designacao e um tipo. No exemplo dado as variaveis i e r sao do tipo inteiro. No seguimento,
apresentar-se-ao outros tipos de variaveis.
• o ponto e vırgula “;” e usado como terminacao de instrucoes.
• a execucao do programa e feita de forma sequencial em que as instrucoes sao separadas por
“;” com a interpretacao dada anteriormente.
• a instrucao i = 2 designa-se por atribuicao e consiste em dar a variavel i o valor 2.
• os ciclos ”while”tem a estrutura while(gurada)corpo em que a guarda sera, em muitos
casos, uma condicao de verificacao que dependera de uma ou mais variaveis que devem ser
atualizadas dentro do corpo. O corpo e o local de escrita de comando a executar sempre que a
condicao na guarda se verifique.
Antes de avancarmos, e conveniente frisar que Java e uma linguagem fortemente tipada, isto e, se se
declarar que uma variavel e inteira entao ela nao pode, em momento algum do programa, assumir
valores que nao sejam inteiros.
13
2.2 Funcoes
SECCAO 2.2
Funcoes
Tal como se referiu um programa em Java e idealizado sobretudo com recursos a funcoes. Apresenta-se
entao a forma basica de uma funcao em Java:
1 public static tipo de retorno nomefuncao (tipo1 var1 ,... ,tipon varn)
2 declaracao de var locais
3 ...
4 instrucoes;
5 ...
6 return expressao
7
em que e necessario observar o seguinte:
1. qualquer funcao devolve um valor e portanto e necessario dizer logo a partida que tipo de dado e
devolvido. Isso e feito definindo “tipo de retorno” semelhante a declaracao de variaveis. No caso
de nao se querer que um resultado seja devolvido deve declara-se o tipo como “void”.
2. toda a funcao tem um nome.
3. pode receber varios dados de diferentes tipos que e necessario listar como argumentos da funcao.
4. similarmente ao que foi dito para o main, ha que declarar as variaveis, executar as instrucoes.
5. por ultimo e necessario devolver um valor que pode ser dado por uma expressao atraves do
comando return.
6. se existir um comando return entao qualquer instrucao elaborada posteriormente e ignorada.
E conveniente observar que a primeira funcao a ser executada de um programa em Java e sempre a
funcao ”main”. No que se segue recupera-se o exemplo do calculo do fatorial de 10 mas desta vez
escrito como funcao.
14
Capıtulo 2. Pacotes utilitarios em Java
Exemplo 2.2
1 class Utils
2 // Exemplo: funcao factorial
3 public static int factorial(int k)
4 int i,r;
5 r=1;
6 i=2;
7 while(i<=k)
8 r=r*i;
9 i++;
10
11 return r; // Retorno da funcao
12
13 public static void main(String args [])
14 int i=10;
15 System.out.println("A solucao e’:" +factorial (10));
16
17
Note-se que a instrucao i++ e equivalente a atribuicao i = i + 1, mas como se explicar mais a
frente esta forma de atribuicao/atualizacao e computacionalmente mais rapida. Convida-se o leitor
a verificar que na realidade, o programa apresentado devolve como resultado 10!.
2.2.1 Efeitos colaterais
As funcoes tem acesso aos argumentos que lhes sao passados mas nao alteram os seus valores no local
onde foram chamadas. A este tipo de passagem da-se o nome de Passagem por valor!
As funcoes podem alterar as variaveis globais (para alem dos argumentos e variaveis locais).Todos os
tipos nao primitivos (classes), que se explicara nas na seccoes subsequentes o que sao, sao passados por
referencia e podem ser alterados. (Na pratica e uma passagem por valor enviando o endereco). A este
tipo de passagem da-se o nome de Passagem por referencia!
15
2.2 Funcoes
Em suma, se os dados forem do tipo primitivo, quando sao dados a funcao, sao copiados e o seu valor
na funcao principal nao se altera mesmo que usados dentro da funcao. No caso dos dados nao serem
primitivos sao passados (e nao copiados) e se alterados dentro da funcao sao globalmente alterados.
Exemplo 2.3
1 public class Utils
2 static public int r=0;
3 static public int numeropares(int n)
4 // quando a funcao efeito e’ chamada no programa principal main a
5 // variavel n domain e’ copiada para o argumento n de efeito
6 int j=n;
7 n=0; // o valor e’ alterado apenas no bloco da funcao e nao no main
8 while(j>=0)
9 if(j%2==0) r++; // a variavel r e’ incrementada sempre que a
10 // funcao efeito e’ chamada
11 j--;
12
13 return n;
14
15 static public void main(String args [])
16 int n=10;
17 numeropares(n);
18 numeropares (2*n);
19 System.out.println ("O n e’ " +n+ " e o r e’ " + r);
20
21
Se se correr o programa seguinte o resultado que se obtem e
O n e 10 e o valor de r e 17.
16
Capıtulo 2. Pacotes utilitarios em Java
SECCAO 2.3
Tipos de dados primitivos
Expoe-se agora os tipos fundamentais que se pode atribuir a uma variavel de um programa de Java:
char que corresponde a um caratere (escrito em codigo Unicode 16);
int que corresponde a um inteiro (com 32bits, o que equivale a representar numeros entre −231 e
231 − 1);
long que corresponde a um inteiro com uma representacao de 64bits;
float que corresponde a um numero de vırgula flutuante de precisao simples;
double que corresponde a um numero de vırgula flutuante de precisao dupla;
boolean que corresponde a um valor logico de false e true;
Existem outros tipos de variaveis que se podem utilizar em Java, nomeadamente vetores, matrizes e
tensores, que serao objeto de reflexao mais a frente. Na tabela seguinte esta a representacao em ASCII
de alguns caracteres.
17
2.4 Tipos e expressoes
Figura 2.1: Tabela de codigo de ASCII de alguns caracteres.
SECCAO 2.4
Tipos e expressoes
Os operadores mais comummente utilizados sao:
Operadores aritmeticos dos quais se salienta:
soma + ;
produto ∗;
subtracao − ;
divisao / que e inteira no caso das variaveis recebidas serem inteiras;
resto da divisao inteira %;
18
Capıtulo 2. Pacotes utilitarios em Java
Predicados e operadores logicos dos quais se salienta:
maior >;
maior ou igual ≥;
menor <;
menor ou igual ≥;
igual ==;
diferente %;
e &&;
ou ||;
not !.
Note-se que por exemplo 5/2 = 2 pois os dois tipos de dados sao inteiros, mas 5.0/2 = 2.5 pois neste
caso, prevalece o tipo de dado mais abrangente, no caso, float.
SECCAO 2.5
Composicao alternativa
Existem algumas formas alternativas de fazer composicao alternativa. Algumas dessas formas listam-se
de seguida:
1 if (guarda)
2 instrucao;
3
Neste primeiro exemplo executa-se a instrucao se a condicao da guarda for verdadeira e nao se executa
nada caso a condicao da guarda seja falsa.
19
2.5 Composicao alternativa
1 if (guarda)
2 instrucao 1
3 else
4 instrucao 2
5
No segundo exemplo executa-se a instrucao se a condicao da guarda for verdadeira e e executada outra
instrucao caso a condicao da guarda seja falsa.
1 if (guarda 1)
2 instrucao 1
3 else if (guarda 2)
4 instrucao 2
5 else
6 instrucao 3
7
Aqui executa-se a instrucao da primeira guarda cuja condicao se verifique e nao se faz nada caso
contrario.
1 if (guarda1)
2 instrucao 1
3 else if (guarda2)
4 instrucao 2
5 else if ...
6 ...
7 else if (guardan)
8 instrucao n
9
20
Capıtulo 2. Pacotes utilitarios em Java
Por fim, executa-se a instrucao da primeira guarda cuja condicao se verifique e e executada outra
instrucao caso a condicao da guarda seja falsa.
Todas estas formas de estruturar uma condicao alternativa sao validas e uteis em problemas concretos.
A sua utilizacao claramente depende do proposito pretendido.
A tıtulo de exemplo de uso desta estrutura apresenta-se de seguida a funcao de Collatz. Para o leitor
interessado, refere-se que a funcao de Collatz e tal que ainda ninguem provou ou refutou o facto de esta
terminar sempre qualquer que seja o dado inicial.
Exemplo 2.4 (Funcao de Collatz)
Nesta funcao a acao depende do valor intermedio calculado. A computacao termina quando o valor
da computacao for igual a 1. Se o valor for par, divide-se por dois e caso contrario, isto e, se o valor
for ımpar, multiplica-se por 3 e soma-se 1. Note-se que para testar se um numero e ımpar basta
verificar se o resto da divisao inteira por 2 e igual a 1.
1 class Utils
2 // Exemplo: Problema 3n+1 - conjectura de Collatz
3 public static void collatz(int n)
4 while(n!=1)
5 if ( n % 2==1) n=3*n+1;
6 else n=n/2;
7
8
9 // main: chamada ’a funcao de collatz
10
Ate a data de publicacao destas notas sabe-se que para todo o numero ate 20× 258 a funcao de Collatz
termina. Os tempos de execucao desta funcao estao retratados no grafico abaixo:
21
2.6 Atribuicoes Quick and Dirty
SECCAO 2.6
Atribuicoes Quick and Dirty
A expressao ”quick and dirty”e usada com o intuito de se referir uma qualquer uma forma facil e
abreviada e nao tao legıvel de uma expressao que tira partido das imperfeicoes da propria linguagem
de programacao. O Java e tambem uma linguagem que permite tais abreviaturas. Sao ”quick”porque,
de modo geral, sao mais rapidas quando comparadas com as expressoes mais completas que abreviam.
Sao ”dirty”porque podem tornar o codigo de leitura difıcil. Uma boa programacao evita ambiguidades
dando primazia a legibilidade do codigo.
Por norma, a forma mais legıvel de escrever uma atribuicao e faze-lo atraves do comando i=express~ao.
22
Capıtulo 2. Pacotes utilitarios em Java
Contudo algumas destas expressoes podem ser abreviadas:
1. i ++, que retorna i e incrementa i;
2. ++ i que incrementa i e retorna i incrementado;
3. i−− e −− i com as respetivas interpretacoes analogas as anteriores;
4. i+ = 10 que retorna i e incrementa i de dez unidades.
5. i∗ = 10 e i− = 10 com as respetivas interpretacoes analogas as anteriores;
A linguagem Java herdou muito Quick and dirty. Outros exemplos sao:
• while(1) que representa num ciclo infinito;
• while(i < 0)i++ em que o significado e simplesmente um while com uma instrucao unica e que
pode ser usado tambem noutras estruturas. Refere-se ao leitor o cuidado de este tipo de fenomenos
apenas executar repetidamente uma instrucao.
• inti=20; while (i–);
SECCAO 2.7
Recursao em Java
Como qualquer outra linguagem de programacao, Java permite o uso de recursao, isto e, a chamada
iterativa de uma funcao dentro da propria funcao.
No exemplo seguinte ilustra-se o uso de recursao para o calculo de um elemento da sucessao de Fibo-
nacci. Recorde-se que se:
f ib(n) =
1 se n=1,2
f ib(n− 1) + f ib(n− 2) caso contrario
23
2.8 Declaracao de vetores e matrizes e tensores
Exemplo 2.5 (Termos da sucessao de Fibonacci)
1 class Utils
2 // Exemplo: Fibonacci
3 static public int fib(int j )
4 if( j<=1) return j;
5 else return fib(j-1)+fib(j-2);
6
7
8 static public void main(String args [])
9 int n=10;
10 System.out.println ("fib("+ n +")=" + fib(n));
11
12
SECCAO 2.8
Declaracao de vetores e matrizes e tensores
Para declarar que uma variavel e um vetor ou uma matriz ou um tensao com um certo tipo de dado
basta considerar:
• int[] a = new int[10];
• double[] V = new double[20], W = new double[20];
• double[][] A = new double[20][20];
• double[][][] T = new double[10][10][10];
De notar que e necessario inicializar a variavel e atribuir-lhe uma dimensao.
Para aceder ao 1o e ao ultimo valor do vetor V considera-se respetivamente V[0] e V[19]. De forma
semelhante a outras variaveis se se quiser passar como argumento a uma funcao, faz-se da seguinte
24
Capıtulo 2. Pacotes utilitarios em Java
forma:
double innerproduct(double V[], double W[], int size)
Exemplo 2.6 (Produto interno)
1 double innerprod (double V[] , double W[])
2 int i=0,size=V.length;
3 double r=0.0;
4 while(i<size)
5 r+=V[i]*W[i];
6 i++;
7
8 return r;
9
Note-se que contrariamente a linguagem C podemos determinar o tamanho dos vetores atraves do
comando V.length.
SECCAO 2.9
Notacao Assimptotica
No calculo do tempo que um dado algoritmo demora que se estudara durante o curso e importante,
nao determinar exatamente para cada instancia o tempo exato, mas sim o tempo assimptotico, isto
e, quando se consideram instancias de um tamanho arbitrariamente grande quanto tempo demora, no
maximo, o algoritmo a terminar quando comparado com o tamanho da instancia. E comum na literatura
considerar-se a notacao O-grande para a analise da complexidade de algoritmos.
Definicao 2.1
Sejam f e g duas funcoes de domınio N. Diz-se que f (n) ∈ O(g(n)) se e somente se existe uma
constante k ∈ N e uma ordem n0 tais que para todo o n ≥ n0 se tem que | f (n)| ≤ |k · g(n)|.
Existem outras definicoes tambem elas importantes que o leitor interessado pode consultar no Capıtulo
3 em [CSRL01]. Desta definicao decorrem os seguintes casos particulares de especial relevo:
25
2.10 Custos das operacoes em Java
Constante O(1);
Logarıtmico O(log n);
Linear O(n);
Quadratico O(n2);
Polinomial O(nc) para algum c ∈ N;
Exponencial O(cn) para algum c ∈ N;
SECCAO 2.10
Custos das operacoes em Java
Como se sabe, um algoritmo e uma composicao de instrucoes, comparacoes e operacoes aritmeticas.
Todas estas partes do algoritmo tem associadas um custo, em termos de tempo computacional. Portanto,
na analise de tempos de execucao e necessario determinar o custo destas operacoes basicas. Em Java:
1. Comparar inteiros ou vırgulas flutuantes tem um custo constante: O(1);
2. Operacoes aritmeticas sobre inteiros ou vırgulas flutuantes: O(1);
3. Aceder a um valor de um vetor ou matriz: O(1);
4. Adicao de inteiros, tem custo linear: O(n) onde n e o numero de bits dos inteiros;
5. Multiplicacao de inteiros, tem custo quadratico: O(n2) onde n e o numero de bits dos inteiros;
6. Resto da divisao, tem custo quadratico: O(n2) onde n e o numero de bits dos inteiros;
26
AULA
3Analise da correcao e complexidade de
algoritmos imperativos: ordenacao
• Nocao de invariante e variante de um ciclo;
• Prova (informal) da correcao de algoritmos imperativos;
• Algoritmos de ordenacao;
• Analise da complexidade dos algoritmos de ordenacao;
• Analise do pior caso;
• Analise de caso medio – suposicoes probabilısticas
27
3.1 Nocao de invariante e variante de um ciclo
SECCAO 3.1
Nocao de invariante e variante de um ciclo
A correcao e a terminacao dos algoritmos sao topicos fulcrais nesta cadeira. A analise de ambos passa
por encontrar propriedades relevantes dos ciclos que compoe o algoritmo que se mantem quando essas
partes do algoritmo sao executadas. Essas propriedades designam-se por invariantes de ciclos e podem
ser definidos na Logica de Hoare pela seguinte regra:
C ∧ IbodyIIwhile(C)body¬C ∧ I
Em jeito de interpretacao desta regra e da relacao com a correccao pode-se definir de forma informal de
invariante como se segue
Definicao 3.1(informal de invariante)
Uma assercao I diz-se um invariante para um ciclo while(G) corpo se, quando for verificada no
inıcio do ciclo, tambem e verificada no fim de cada iteracao do ciclo.
Assim, um algoritmo que envolve ciclos esta correcto se partindo do princıpio que o invariante e valido
antes da execucao do ciclo e, sempre que o invariante do ciclo e valido antes de cada iteracao, tambem
o e depois dessa iteracao.
Sem entrar em muito detalhe menciona-se que para tornar esta definicao rigorosa era necessario definir
os seguintes termos:
1. Estado do programa (valores das variaveis + estado do controlo);
2. Sintaxe da logica para os invariantes (primeira-ordem com predicados relevantes);
3. Semantica;
Informalmente entende-se por “estado” a configuracao do programa, isto e, o valor das variaveis e o
program counter (que comando esta a ser executado).
Nesta disciplina opta-se, como e comum na literatura, por utilizar linguagem matematica informal para
analisar os algoritmos imperativos nao sendo necessario recorrer a definicao formal de correcao de
um algoritmo para demonstrar que este realmente funciona. E esperado apenas que o leitor consiga
28
Capıtulo 3. Analise da correcao e complexidade de algoritmos imperativos: ordenacao
identificar um bom invariante, isto e, que esteja relacionado com a construcao do ciclo, e demonstre que
o invariante realmente funciona.
No exemplo que se segue explora-se esta abordagem encontrando um invariante para o calculo do
factorial com um ciclo decrescente.
Exemplo 3.1 (Factorial)
1 public static int factorial(int k)
2 int i,r;
3 r=1;
4 i=k+1;
5 while(i>2)
6 i--
7 r=r*i;
8
9 return r;
10
um invariante para o ciclo while e:
r =n
∏k=i
k ∧ i > 1
Note-se que no inıcio do ciclo i = n+ 1, e portanto o produtorio transforma-se emn
∏k=i
k =n
∏k=n+1
k = 1
que coincide com o valor de r. Alem disso claramente i ≥ 1. Por outro, supondo que no inıcio de
uma iteracao i > 2 do ciclo, o valor de r en
∏k=i+1
k entao no fim dessa iteracao obtem-se que o novo
valor de r e ((i + 1)− 1)× r = in
∏k=i+1
k =n
∏k=i
k. No caso em que a guarda falha, isto e, quando i ≤ 2,
significa que i−− obtido da iteracao anterior foi 2, e portanto em qualquer dos casos, i ≥ 1 como se
pretendia. Assim demonstra-se de forma informal a seguinte proposicao:
Proposicao 3.2
Se o programa anterior terminar entao este calcula o fatorial de um numero.
29
3.1 Nocao de invariante e variante de um ciclo
Definicao 3.2
Uma relacao (A,<) e bem fundada se qualquer subconjunto nao vazio B de A tem elemento mini-
mal.
Exemplo 3.3
Claramente (N,<) e uma relacao bem fundada. Outro exemplo de uma relacao bem fundada e a
relacao (0, 1, 1, 2, 0, 1, 2,⊆) sendo os seus elementos minimais 0, 1, 1, 2
Definicao 3.3
Um variante de um ciclo while(G)body e invariante I e um mapa dos “estados” que satisfazem I
para o suporte A de uma relacao bem fundada (A,<) cujo valor decresce estritamente em relacao a
< por cada iterada do ciclo.
Daqui pode-se deduzir uma regra da correcao total, que no calculo de Hoare se expressa atraves do
predicado:
< e bem fundada, [I ∧ C ∧V = z]S[I ∧V = z][I] while C do S[I ∧ ¬C]
.
Teorema 3.1
Dado um invariante I, um ciclo while termina sse existe um variante para esse ciclo e para esse
invariante I.
Definicao 3.4
Um variante diz-se natural se o suporte da relacao bem-fundada for um subconjunto dos numeros
naturais.
Teorema 3.2
Ciclos while com variantes naturais nao sao universais.
O teorema anterior e um resultado negativo muito importante. Note que nesta disciplina todos os
procedimentos estudados sao algoritmos e, o problema da terminacao destes e simples e tem (quase
sempre) variante natural. Serve no entanto de aviso que nem todos os algoritmos admitem variantes
naturais.
30
Capıtulo 3. Analise da correcao e complexidade de algoritmos imperativos: ordenacao
SECCAO 3.2
Algoritmos de ordenacao e complexidade no pior caso e caso medio
Nesta seccao apresentam-se alguns dos algoritmos de ordenacao mais comummente utilizados como
instrumento utilitario para introduzir e explorar conceitos importantes de algoritmia que fazem parte
da estrutura curricular do curso. Sugere-se a leitura cuidado do Capıtulo 2 do livro [CSRL01] para um
estudo completo dos topicos aqui referidos.
O problema de ordenacao e frequente e recorrente na pratica. Formalmente, define-se este problema da
seguinte forma:
Problema 3.4 (Problema de Ordenacao)
Instancia Dado um vetor de numeros (a1, ..., an);
Solucao Uma permutacao dos elementos desse vetor (a′1, ..., a′n) de tal modo a que os elementos
estejam ordenados.
Por exemplo, dado o vector
(31; 41; 59; 26; 41; 58),
pretende-se obter o vector reordenado
(26; 31; 41; 41; 58; 59).
No que se segue apresentam-se varios algoritmos que resolvem este problema.
3.2.1 Algoritmo de Insercao
Como o nome indica, o que o algoritmo faz e inserir um nome elemento e ordenar os elementos em mao.
O algoritmo pode ser escrito em Java com o seguinte codigo:
31
3.2 Algoritmos de ordenacao e complexidade no pior caso e caso medio
1 public static void insertionSort(int V[])
2 int i=1, j, aux ,n=V.length;
3 while(i < n)
4 aux=V[i];
5 j = i-1;
6 while(j>=0 && V[j] >aux)
7 V[j+1] = V[j];
8 j--;
9
10 V[j+1] = aux;
11 i++;
12
13
Deixa-se ao cuidado do aluno provar a seguinte proposicao:
Proposicao 3.5
Se o programa anterior terminar entao este retorna um vetor ordenado.
Para tal sugere-se que o leitor verifique que para o ciclo interior o predicado:
Os valores V[j + 1], .., V[i] ≥ aux estao ordenados e i ≤ n
e um invariante e que o predicado:
Os valores V[0]...V[i− 1] estao ordenados e i ≤ n
e um invariante do ciclo exterior.
Como ja foi referido numa das aulas anteriores, e importante saber quanto tempo (ou espaco) um
algoritmo demora (consome) ate terminar a computacao. A esta analise, do tempo de execucao, chama-
se analise da complexidade temporal de um algoritmo. Existem outro tipos de analise de complexidade
que saem do ambito desta cadeira.
Recorde-se, da ultima aula, em que foi apresentado o custo basico das operacoes em Java. Como as
operacoes que nao dependem de ciclos sao todas constantes, no pior caso, pode-se dizer que dado um i
32
Capıtulo 3. Analise da correcao e complexidade de algoritmos imperativos: ordenacao
o ciclo interior sera percorrido i vezes e o ciclo exterior sera percorrido n− 1 vezes, logo, no pior caso o
tempo de execucao do algoritmo e:
O
(n−1
∑i=1
c1 +i−1
∑j=0
c2
)= O
(c
n−1
∑i=1
i
)= O
( c2
n(n− 1))= O(n2)
O pior caso acontece quando o vetor ja esta ordenado mas por ordem decrescente. No melhor caso, que
acontece quando o ciclo interior nao e percorrido vez nenhuma em cada i, ou seja, quando o vetor ja
esta ordenado por ordem crescente, o tempo de execucao e:
O
(n−1
∑i=1
c
)= O (c(n− 1)) = O(n)
Comparando os dois casos, verifica-se uma diferenca substancial entre o melhor e o piro caso.
Pode-se ainda fazer outro tipo de analise temporal baseada no calculo da media. Tal analise designa-se
por analise no caso medio. Esta nocao permite ter uma ideia de como e o comportamento aproximado
do algoritmo numa qualquer situacao. Pode acontecer que as instancias que conduzem ao pior caso,
nunca ocorram e portanto a analise do pior caso seja pouco verosımil.
Contudo, para se proceder a analise do caso medio e necessario fazer suposicoes probabilısticas sobre
as entradas. Assim, diferentes suposicoes podem levar a diferentes comportamentos medios.
A analise do caso medio corresponde a calcular o valor esperado da complexidade temporal tendo em
linha de conta a distribuicao de probabilidades das entradas.
Exemplo 3.6
Retomando o exemplo do algoritmo de insercao, dado que nada se sabe sobre a distribuicao das
entradas assume-se que o ciclo interior podera terminar com j = −1, 0, . . . ., i− 1 equiprovavelmente.
Assim, dado j o ciclo sera percorrido i− j + 1 vezes e como ha i + 1 possibilidades para o valor de j
conclui-se que a media de tempo gasta em cada ciclo interior e:
i−1
∑j=−1
(i− j + 1)p(j) =i
∑j=0
(i− j)p(j)
=i
∑j=0
jp(j)
=i
∑j=0
ji + 1
=i2
.
Logo, em media, o tempo de execucao do algoritmo de insercao e:
33
3.2 Algoritmos de ordenacao e complexidade no pior caso e caso medio
O
(n−1
∑i=1
c1 + c2i2
)= O
(n−1
∑i=1
i2
)= O
(14
n(n− 1))= O(n2).
3.2.2 Algoritmo Bubblesort
O proximo algoritmo de ordenacao designa-se por Bubblesort pois tem as suas raızes na diferenca de
potencial de bolhas de ar, que quando o volume de ar for maior deixa fluir para a bolhas mais pequena
trocando de posicao.
1 class Node
2 int val;
3 Node next;
4 ;
5
6 public class Lista
7 int comp;
8 Node prim;
9 ...
10 ;
Deixa-se ao cuidado do aluno mostrar as seguintes proposicoes:
Proposicao 3.7
O algoritmo Bubblesort esta correto e para, isto e, termina sempre e no fim produz como resultado
um vector ordenado.
Sugere-se a utilizacao dos invariantes:
V[n− i + 1]...V[n− 1] esta ordenado e tem os maiores elementos de V e i ≤ n + 1
e
V[j] e o maior valor de V[0]...V[j] de V e j ≤ n− i
34
Capıtulo 3. Analise da correcao e complexidade de algoritmos imperativos: ordenacao
Proposicao 3.8
O algoritmo Bubblesort tem complexidade do pior caso, melhor caso e caso medio igual a O(n2).
35
AULA
4Analise de algoritmos recursivos: Quicksort
• Quicksort;
• Notacao assimptotica Ω e Θ;
• Arvores de recursao;
• Solucao de recorrencias e o Teorema Mestre;
• Analise do algoritmo de Quicksort;
– Analise no melhor caso;
– Analise no pior caso;
– Analise no caso medio.
37
4.1 Continuacao dos algoritmos de ordenacao
SECCAO 4.1
Continuacao dos algoritmos de ordenacao
Nesta seccao analisar-se-a um outro algoritmo de ordenacao que apesar de ser mais complexo de enten-
der os seus passos e menos custoso computacionalmente, isto e, com melhores performances em termos
de tempo, como veremos.
4.1.1 Algoritmo QuickSort
Este algoritmo foi inventado por Hoare em 1960 enquanto aluno da Universidade de Moscovo a sua
publicacao data de 1962 apos uma serie de refinamentos. O codigo Java do QuickSort e o seguinte:
O algoritmo Quicksort tem por base uma estrategia conhecida por “divisao e conquista que basicamente
consiste em dividir o problema em problemas mais pequenos. Seguidamente o Quicksort ordena, ou
seja, “conquista” as sublistas de menores e maiores recursivamente ate que a lista completa se encontre
ordenada. Resumidamente os passos deste algoritmo de ordenacao sao:
1. Escolhe-se um pivot, que e um elemento da lista:
2. Troca-se os elementos da lista de forma que todos os elementos anteriores ao pivot sejam menores
que ele, e todos os elementos posteriores ao pivot sejam maiores que ele.
3. No final do processo o pivot estara na sua posicao final e havera duas sublistas possivelmente nao
ordenadas. Essa operacao e denominada de particao;
4. Recursivamente ordena a sublista dos elementos menores e a sublista dos elementos maiores
escolhendo novos pivots;
Note-se a base da recursao deste algoritmo sao as listas de tamanho sem elementos ou com um unico
elemento. Por essa razao estao claramente ordenadas. Note-se tambem que este algoritmo e finito,
pois a cada iteracao ha pelo menos um elemento que e colocado na sua posicao final e nao mais sera
manipulado na iteracao seguinte.
38
Capıtulo 4. Analise de algoritmos recursivos: Quicksort
1 public static void main(String [] args)
2
3 void quickSort(int vec[], int left , int right)
4 int r;
5 if (right > left)
6 r = partition(vec , left , right); /* pivot */
7 quickSort(vec , left , r - 1);
8 quickSort(vec , r + 1, right);
9
10
11
12 int partition(int vec[], int left , int right)
13 int i, j;
14 i = left;
15 for (j = left + 1; j <= right; ++j)
16 if (vec[j] < vec[left])
17 ++i;
18 swap(vec ,i,j);
19
20
21 swap(vec ,left , i);
22 return i;
23
24
25 void swap(int vec[] a, b)
26 int tmp;
27 tmp = vec[a];
28 vec[a] = vec[b];
29 vec[b] = tmp;
30
31
32
Na figura que se segue ilustra-se o comportamento do procedimento partition. A entrada vec[r] e o pivot.
As celulas sombreadas ao de leva sao elementos do vetor e sao parte a primeira particao com elementos
39
4.1 Continuacao dos algoritmos de ordenacao
nao maiores que vec[r]. As celulas mais escuras sao elementos da segunda particao com valores maiores
que vec[r].Os elementos brancos ainda nao fizeram parte de uma das particao e o ultimo elemento sem
sombra e o pivot. (a) vector inicial e a consideracao das variaveis. Sem que nenhum dos elementos
esteja numa das duas particoes. (b) O valor 2 e “trocado com ele proprio” e colocado na particao dos
valores menores. (c)–(d) Os valores 8 e 7 sao adicionados a particao dos valores maiores. (e) Os valores
1 e 8 sao “trocados” e a particao dos menores cresce. (f) Os valores 3 e 7 sao trocados, e a particao dos
menores cresce. (g)–(h) A particao dos maiores cresce para incluir 5 e 6, e o ciclo termina. (i) Nas linhas
7–8, o elemento pivot e “trocado” de modo a ficar entre as duas particoes.
Figura 4.1: Exemplo de aplicacao do processo de particao
No que se segue mostra-se por inducao sobre os invariantes que o algoritmo Quicksort esta correcto, ou
seja, faz exactamente o que devia fazer.
40
Capıtulo 4. Analise de algoritmos recursivos: Quicksort
Proposicao 4.1
O algoritmo Quicksort apresentado na pagina ?? esta correto.
A prova desta proposicao passa por mostrar que
Demonstracao : Para a base (n = 1): Qualquer vetor com apenas um elemento esta claramente ordenado,
Hipotese de Inducao: Suponha que para vetores de tamanho n− 1 o algoritmo Quicksort ordena bem os elemen-
tos.
Suponha que o procedimento partition esta a funcionar corretamente, isto e, que a direita do pivot so ha elementos
maiores que o pivot e a esquerda so ha elementos menores ou iguais ao pivot. Como quickSort(vec, le f t, r − 1)
e quickSort(vec, r + 1, right) sao vectores de dimensao no maximo n − 1 (pois r e no maximo n) por hipotese
de inducao ficam ordenados depois da aplicacao do Quicksort. Por outro lado, como o pivot fica exactamente
na posicao r, e qualquer elemento a sua esquerda, isto e com ındice menor, e menor que o pivot, e qualquer
elemento a sua direita, isto e com ındice maior, e maior que o pivot, entao conclui-se que o algoritmo quicksort
esta implementado corretamente.
Deixa-se ao cuidado do leitor verificar que o procedimento partition esta correto indicando os seus invariantes e
demonstrando que realmente sao invariantes.
Antes de se avancar com a analise da complexidade do algoritmo Quicksort complementam-se as nocoes
do notacao O-grande introduzidas na segunda aula. Recorde-se que
Definicao 4.1
Sejam f e g duas funcoes de domınio N. Diz-se que f (n) ∈ O(g(n)) se e somente se existe uma
constante k ∈ N e uma ordem n0 tais que para todo o n ≥ n0 se tem que | f (n)| ≤ |k · g(n)|.
Tal como foi referido na altura, existem outras nocoes tambem elas importantes. Sao o caso da nocao
de limite inferior Ω e exata Θ. Como os proprios nomes indicam, Ω refere-se a um comportamento que
minimiza assimptoticamente a funcao e Θ refere-se a um comportamento assimptoticamente exato da
funcao.
As definicoes formais que serao usadas durante o curso e em especial nesta aula sao as que se descrevem
a seguir:
Definicao 4.2(Notacao Omega)
Sejam f e g duas funcoes de domınio N. Diz-se que f (n) ∈ Ω(g(n)) se e somente se existe uma
constante k ∈ N e uma ordem n0 tais que para todo o n ≥ n0 se tem que |k · g(n)| ≤ | f (n)|.
41
4.2 Arvores de recursao e o Teorema Mestre
Definicao 4.3(Notacao Theta)
Sejam f e g duas funcoes de domınio N. Diz-se que f (n) ∈ Θ(g(n)) se e somente se f ∈ O(g) e
g ∈ O( f ) ou seja se existem constantes k1, k2 ∈ N e uma ordem n0 tais que para todo o n ≥ n0 se tem
que |k1 · g(n)| ≤ | f (n)| ≤ |k2 · g(n)|.
Figura 4.2: Esquema ilustrativos dos varios tipos de enquadramentos das funcoes.
SECCAO 4.2
Arvores de recursao e o Teorema Mestre
Ja se referiu que recursao e o processo que um algoritmo quando um dos passos do procedimento em
questao envolve a repeticao completa deste mesmo procedimento.
As arvores de recursao sao uma forma esquematica de representar a informacao sobre a recursao que
permite visualizar o esforco computacional de todo o processo recursivo e estabelecer limites para este
esforco.
No exemplos que se seguem considere-se que T(n) e o tempo de necessario para uma recursao com
uma entrada de tamanho n.
Exemplo 4.2 (Recursao da forma T(n) = 2T(n/2) + n2)
Da observacao da figura seguinte, dado que a arvore e balancada, concluımos que ha log n nıveis
(explique porque esta profundidade!) e no nıvel i o custo total de chamada e n2/2i. Logo:
T(n) = O
(n2
log n−1
∑i=0
1/2i
)= O
(n2 ×O(1)
)= O(n2).
42
Capıtulo 4. Analise de algoritmos recursivos: Quicksort
Figura 4.3: Exemplo de uma arvore de recursao.
Exemplo 4.3 (Recursao da forma T(n) = T(n/3) ++T(2n/3) + n)
Figura 4.4: Exemplo de uma arvore de recursao.
Observando a figura comece por explicar o porque da profundidade ser log3/2 n. Observe que a
arvore nao e totalmente balancada pois o custo de um dos lados e superior ao do outro. Contudo o
custo cada nıvel de chamada da recursao e n. Logo:
T(n) = O
nlog3/2 n−1
∑i=0
1
= O (n×O(log n)) = O(n log n).
No teorema seguinte estabelece-se de que forma podemos resolver recorrencias de forma sistematica!
Este teorema e uma ferramenta importante na analise assimptotica de recorrencias pois a maioria das
recorrencias que sao usadas em algoritmos ditos de ”divisao e conquista”(divide and concur) que usu-
43
4.2 Arvores de recursao e o Teorema Mestre
almente se estudam estao nas condicoes do teorema. E conveniente frisar que nem todas as recorrencias
se podem resolver usando este teorema.
Teorema 4.1
Sejam a ≥ 1 e b > 1 constantes, f e T duas funcoes definidas sobre os numeros naturais que
satisfazem a seguinte relacao de recorrencia:
T(n) = aT(n/b) + f (n)
em que n/b = bn/bc ou n/b = dn/be. Entao T(n) pode ser limitado da seguinte forma:
• se f (n) ∈ O(
nlogb a−ε)
para algum ε > 0 entao T(n) ∈ O(nlogb a);
• se f (n) ∈ Θ(
nlogb a)
entao T(n) ∈ Θ(nlogb a log n);
• se f (n) ∈ Ω(
nlogb a+ε)
para algum ε > 0 e se a f (n/b) ≤ c f (n) para alguma constante c < 1
entao T(n) ∈ O(nlogb a).
Aqui apresenta-se um esquisso da prova do teorema deixando-se a prova formal e completa com detalhe
ao cuidado do leitor que podera, no entanto consulta-la em [CSRL01]. Note-se que o resultado da
recorrencia depende da comparacao entre f (n) e (nlogb a). Note ainda que a diferenca entre esses valores
tem que ser polinomial e portanto o teorema mestre nao se aplica a todas recorrencias. Usar-se-a este
Teorema na proxima seccao para determinar a complexidade do Quicksort.
Demonstracao Esquisso da prove : Apresenta-se a prova para o caso em que se assume que n e uma potencia de
b, i.e., n = bi para algum i. Para as potencias de b a recorrencia assume os seguintes valores:
T(n) =
Θ(1) se n = 1
aT(n/b) + f (n) se n = bi
.
Assim, a solucao da recorrencia e:
T(n) = Θ(nlogb a) +logb n−1
∑j=0
aj f (n/bj).
A figura seguinte ajuda a entender o argumento que demonstra esta solucao.
44
Capıtulo 4. Analise de algoritmos recursivos: Quicksort
Figura 4.5: A recorrencia gerada por T(n) = aT(n/b) + f (n). Esta arvore tem nlogb a folhas.
A cada passo da iteracao a arvore bifurca em a ramos e a soma de cada um deles e a f (n/b). No fim ha exatamente
nlogb a folhas cada uma com um custo constante. Cada no interior de profundidade j custa aj f (n/bj) operacoes.
Daqui resulta a relacao descrita.
Apresenta-se a prova da alınea a mostrando que se g(n) =logb n−1
∑j=0
aj f (n/bj) e f (n) ∈ O(nlog ba−ε para algum ε > 0
entao g(n) ∈ O(nlogb a).
Dado que f (n) ∈ O(nlog ba−ε entao f (n/bj) ≤ O((n/bj)log ba−ε. Logo
logb n−1
∑j=0
aj f (n/bj) = nlogb a−εlogb n−1
∑j=0
(abε
blogb a
)j
= nlogb a−εlogb n−1
∑j=0
bεj
= nlogb a−ε
(bε logb n−1
bε−1
)j
≤ O(nlogb a)
Como b e ε sao constantes entao
45
4.3 Analise do algoritmo de Quicksort
SECCAO 4.3
Analise do algoritmo de Quicksort
4.3.1 O melhor caso
O algoritmo Quicksort na melhor das hipoteses corre em tempo da ordem n log n. Este caso acontece
quando o numero de particoes que sera preciso efetuar e mınimo. Assim, o melhor caso ocorre quando a
arvore e balancada, ou seja, quando a particao divide sempre a meio os elementos pelas duas chamadas
recursivas. Assim, a recursao no melhor caso pode ser expressa por:
T(n) = 2T(n/2) + Θ(n).
Aplicando o Teorema mestre com a = b = 2 e f (n) = n obtemos que no melhor caso o Quicksort e linear,
ou seja, se t(n) for o tempo de execucao do Quicksort para listas de tamanho n entao t(n) ∈ Θ(n log n).
4.3.2 O pior caso
No pior caso acontece quando o algoritmo Quicksort ao chamar o procedimento partition e este colocar
todos os elementos numa unica particao, pois nesse caso o numero de chamadas do procedimento
partition e maximo. Assim, a recursao no pior caso do algoritmo Quicksort pode ser expressa por:
T(n) = T(n− 1) + Θ(n).
Esta recursao nao encaixa no Teorema Mestre mas pode ser resolvida facilmente. Note-se que:
T(n) = T(n− 1) + Θ(n)
= T(n− 2) + Θ(n− 1) + Θ(n)
= T(n− 3) + Θ(n− 2) + Θ(n− 1) + Θ(n).
Logo, a recursao toda pode ser expressa por:
46
Capıtulo 4. Analise de algoritmos recursivos: Quicksort
T(n) =n
∑k=1
Θ(k) = Θ
(n
∑k=1
n
)= Θ(n2).
Logo, no pior caso, o Quicksort e quadratico.
4.3.3 O caso medio
Para analisar o caso medio do Quicksort suponha que a separacao que e feita pelo procedimento par-
tition e equiprovavel, ou seja, com igual probabilidade obtem duas particoes, uma com i elementos e
outra com n− 1− i elementos. Logo, a expressao da recursao neste caso e descrita por:
T(n) =1n
(T(0) + T(n− 1) +
n−1
∑i=1
T(i) + T(n− 1− i)
)+ Θ(n).
Como no pior caso T(0), T(n− 1) ≤ O(n) pode-se fazer a seguinte majoracao:
T(n) =1n
(T(0) + T(n− 1) +
n−1
∑i=1
T(i) + T(n− 1− i)
)+ Θ(n)
≤ 1n
(n−1
∑i=1
T(i) + T(n− 1− i)
)+ Θ(n)
=2n
(n−1
∑i=1
T(i)
)+ Θ(n)
Para terminar a prova e necessario os seguinte lema:
Lemma 4.4. Seja T(n) a expressao de recorrencia descrita acima. Entao, existem a, b > 0 tais que para todo o n,
T(n)−Θ(n) ≤ a · n log n + b.
Demonstracao : A prova e feita por inducao sobre n.
(Base n = 1):
Basta escolher a = 1 e b = T(1). Contudo para que os detalhes no passo indutivo sejam coerentes, considera-se
b = T(1) e a tal que 2b− a4
n ≤ b.
Com estas escolhas temos que:
a · n log n + b = a · 1 log 1 + T(1) = T(1) ≥ T(1)−Θ(n).
47
4.3 Analise do algoritmo de Quicksort
(Passo indutivo):
Suponha que para todo o i ≤ n− 1 T(i) ≤ a · n log n + b para alguns a e b positivos. Entao:
T(n)−Θ(n) ≤ 2n
(n−1
∑i=1
T(i)
)+ Θ(n)−Θ(n)
≤ 2n
(n−1
∑i=1
a · i log i + b
)
≤ 2an
c +2bn(n− 1)
≤ 2an
(12
n2 log n− 18
n2)+
2bn(n− 2)
≤ a · n log n− a4
n + 2b
≤ a · n log n + b
A ultima desigualdade decorre da escolha feita de a.
Resta mostrar a desigualdade
n−1
∑i=1
i log i <12
n2 log n− 18
n2.
n−1
∑i=1
i log i ≤ (log n− 1)dn/2e−1
∑i=1
i + log nn−1
∑i=bn/2c
i
= log nn−1
∑i=1
i−dn/2e−1
∑i=1
i
≤ 12
n2 log n− 18
n2
Retomando o calculo da complexidade media tem-se entao que
T(n) ≤ 2n
(n−2
∑i=1
T(i)
)+ Θ(n)
≤ 2an
(12
n2 log n− 18
n2)+ Θ(n)
≤ a · n log n− a4
n + 2b + Θ(n)
≤ O(n log n)
48
AULA
5Tipos de dados abstratos e interfaces em
Java
• Interfaces em Java vs tipos de dados abstratos;
• Classes;
• Implementacao de tipos de dados abstratos.
49
5.1 Tipos de dados abstratos
SECCAO 5.1
Tipos de dados abstratos
Como ja se viu numa das primeiras aulas, O Java tem varios tipos primitivos de dados, como o int.
Cada um destes tipos possuem:
• Conjunto de valores ou domınio;
• Operacoes possıveis entre estes valores;
Por vezes, os tipos de dados primitivos nao permitem, dado um problema concreto, representar de
forma clara os objetos inerentes ao problema. Torna-se entao util definir novas entidades, que se de-
signam de ojetos abstratos. Da mesma forma, que os tipos primitivos, alem dos valores, tem acoes, ao
definirem-se outro tipo de dados, torna-se necessario especificar os seus possıveis valores e as operacoes
que podem ser executadas sobre esses mesmos dados.
O objetivo destes dados e reduzir a informacao necessaria para a criacao/programacao de um algoritmo
atraves da abstracao das variaveis envolvidas criando uma unica entidade fechada que agrega as suas
principais caracterısticas comuns especificando as operacoes proprias que lhe inerentes.
A tıtulo de exemplo consideremos um aluno que se matricula no universidade. Sem tipos de dados
abstratos cada aluno teria de ser representado por variaveis soltas, como nome, idade, localidade, curso,
disciplinas, notas etc. Estas variaveis seriam operadas independentemente sem que houvesse uma
ligacao logica entre elas. Com este tipo de dados abstraem-se as propriedades comuns a todos os alunos
e cria-se uma entidade, comummente designada por tipo, “estudante”. Para potenciar a utilizacao de
tipos abstratos e necessario definir que operacoes e ligacoes logicas existem entre as varias caracterısticas
do tipo.
Um outro exemplo e o de uma fila de inteiros que basicamente consiste como o proprio nome indica
um conjuntos de inteiros (visto com um vetor) que admite insercao de novos elementos e a remocao de
elementos ja existentes, designar o primeiro elemento e especificar se a fila e ou nao vazia.
Uma fila e uma estrutura sujeita a regra de operacao “First In, First Out” que de forma informar significa
que sempre que houver uma remocao, o elemento removido e o que esta na estrutura ha mais tempo,
ou seja, o primeiro objecto inserido na fila e tambem o primeiro a ser removido.
50
Capıtulo 5. Tipos de dados abstratos e interfaces em Java
Como foi referido, as filas tem operacoes e predicados que podem ser vistas como funcoes. A tıtulo de
exemplo expositorio considere-se assinatura (da designacao das operacoes, tipos dos seus argumentos,
tipos de resultado):
• nova, que partindo do vazio, cria uma nova fila;
• insere, que partindo de uma fila nao vazia e um inteiro retorna uma fila
• primeiro, que dada uma fila devolve um inteiro, no caso o primeiro inteiro da fila;
• vazia, que devolve um valor booleano que indica se uma dada fila esta esta vazia;
• retira, que dada uma fila devolve uma outra fila igual a primeira com o primeiro elemento reti-
rado;
Todas estas operacoes sao regidas por relacoes ou regras de calculo, entre elas, que permitem derivar
o resultado das operacoes para cada valor dos argumentos. Estas relacoes sao usualmente designadas
por especificacoes. Por exemplo, quando se opera primeiro(insere(nova, N)), o resultado pretendido e N
dado que o primeiro elemento de uma nova fila em que se inseriu um numero N deve devolver que o
seu primeiro elemento e o proprio N. Para definir mais rigorosamente as relacoes entre elas e necessario
recordar alguns conceitos.
Os termos ou entidades de tipo Fila sao um conjunto definido indutivamente da seguinte forma:
• O conjunto de variaveis X esta contida nos termos de Tfila;
• O operador Op que toma termos t1, ..., tk respetivamente em T1, ..., Tk e devolve uma fila e um termo
em Tfila.
Para uma introducao mais completa e cuidado desta tematica sugere-se a leitura atenta do Capıtulo 9
do livro [CSS+04].
Observe que Tfila e a algebra livremente gerada a partir da assinatura e conjunto de variaveis X.
Uma equacao e uma igualdade entre termos do mesmo tipo
t1 = t2.
51
5.2 Implementacao de tipos de dados abstratos em Java
Uma especificacao equacional e um conjunto de equacoes.
Exemplo 5.1
• primeiro(insere(nova, N)) = N;
• primeiro(insere(insere(W, N1), N2) = primeiro(insere(W, N1));
• vazia(nova) = true
• vazia(insere(W, N)) = f alse
• retira(insere(nova, N)) = nova
• retira(insere(insere(W, N1), N2) = insere(retira(insere(W, N1)), N2)
Deixa-se ao cuidado do leitor verificar e interpretar estas equacoes.
Uma classe e um tipo de dados que contem o molde,isto e a especificacao dos seus elementos (objetos),
a semelhanca de um tipo basico como inteiro que contem o molde para as variaveis declaradas como
inteiros. A classe envolve e associa funcoes e dados, controlando a forma como e feito o acesso a estes.
A sua definicao consiste em especificar os seus atributos (dados) e seus metodos (funcoes).
Um objecto e uma instanciacao de uma classe.
Uma interface e o conjunto de metodos que permitem alterar os atributos dos objetos das classes.
SECCAO 5.2
Implementacao de tipos de dados abstratos em Java
Considera-se o exemplo de implementacao de Fila discutido na seccao anterior com as funcionalidades:
• nova (que pode ser omitido dado que o Java tem um construtor universal);
• insere;
• retira;
• vazia;
52
Capıtulo 5. Tipos de dados abstratos e interfaces em Java
Descrevem-se dois metodos de implementacao: um estatico em que o tamanho dos objetos e definido a
priori e outro em que o tamanho dos objetos e dinamico.
A vantagem de usar este metodo, apesar de ser ligeiramente mais complicado de implementar, e que
apenas aloca memoria a medida que e necessaria permitindo que quando o valor de uma variavel deixa
de ser preciso o java liberta essa memoria. O java tem uma funcionalidade designada de Garbage
collector que vai percorrendo as variaveis e libertando as que nao sao necessarias.
O interface funciona como a parte o projeto de Java que descrimina a assinatura do tipo de dados
abstrato, ou seja, o interface declara que metodos tem os objetos do tipo Fila.
1 public interface Fila
2 // Desnecessario implementar a nova pois ha sempre um construtor new
3 // Todas as funcoes que retornam e recebem uma fila devem retornar
void e retirar essa fila do argumento
4 // Um objecto tem estado e comportamento , estas funcoeses alteram o
estado e naxso retornam nada.
5
6 void insere(int x);
7 int primeiro ();
8 void retira ();
9 boolean vazia();
10
Note-se que os metodos que foram declarados devolvem, de acordo com a funcionalidade que imple-
mentam, um tipo de dados (primitivo).
Dado que o Java e uma plataforma muito usual e muitos utilizadores partilham o seu codigo e costume,
para cada implementacao, criar-se uma especie de pequeno “manual” de funcionamento do codigo,
onde se pretende informar quem utiliza o codigo das funcionalidades que se projetaram e programaram
naquele pedaco de codigo. Essas indicacoes das funcionalidade criadas sao usualmente descritas como
comentarios sucintos com a indicacao do que cada uma delas e suposto fazer e devolver de modo a que
seja percetıvel para o para o programador.
53
5.2 Implementacao de tipos de dados abstratos em Java
Por outro lado, dado que o proposito da programacao industrial passa tambem por ter lucros, nor-
malmente e disponibilizado o codigo compilado estando apenas parte dele visıvel a quem o utiliza.
Esta particularidade tem impacto na forma como estes estruturas sao implementadas (bem como nos
comentarios feitos) e no seguimento far-se-ao alguns comentarios relativos ao codigo propriamente dito
tendo em conta esta visao.
5.2.1 Implementacao estatica de Listas
No que se segue apresenta-se a implementacao estatica de Filas completando o codigo descrito ulterior-
mente.
1 public class Filaest implements Fila
2
3 private int[] vec;
4 private int pos ,max;
5
6 public Filaest(int i) // construtor nova? ver comentario ulterior
7 vec=new int[i];
8 max=i;
9 pos=0;
10
11
12 public void insere(int x)
13 if(pos <max)
14 vec[pos]=x;
15 pos ++;
16
17
18
19 public int primeiro ()
20 if (pos >0) return vec [0];
21 return -1;
22
23
54
Capıtulo 5. Tipos de dados abstratos e interfaces em Java
24 public void retira () // retira ultimo
25 if (pos >0) pos --;
26
27
28 public boolean vazia()
29 return pos ==0;
30
31
Por uma questao de completude e para um melhor entendimento do que se pretende do codigo tecem-se
algumas consideracoes sobre o que foi apresentado:
• Ha variaveis que sao declaradas private e variaveis que sao declaradas como public (e ainda outras
como protected que so as subclasses definidas a custa dela podem aceder e que serao discuti-
das numa das proximas aulas). As primeiras destinam-se a serem utilizadas internamente pela
implementacao nao estando disponıveis para outras partes de um possıvel programa que use esta
implementacao. Apenas o codigo sob a alcada dado implements pode aceder aos seus valores. As
public destinam-se a devolver os resultados das acoes.
• O comentario relativo a construcao de uma nova fila deve-se ao facto de o Java ter in built um
construtor para cada entidade que usa;
• As variaveis pos e max nao sao fundamentais na criacao de uma nova fila, contudo sao prepon-
derantes para os restantes metodos. Por exemplo, se se quiser inserir um novo elemento, se se
souber qual a posicao do ultimo elemento da fila torna-se simples acrescentar um novo elemento.
• No metodo insere e necessario que apos a insercao do novo elemento se atualize a posicao do
ultimo elemento.
• No metodo retira apenas e necessario apontar atualizar a posicao do ultimo elemento, que no caso
passa a ser o penultimo elemento da fila. Note-se que em vez de se retirar o primeiro elemento da
fila esta-se a retirar o ultimo elemento. Deixa-se ao cuidado do leitor implementar o metodo que
retira o primeiro. Devera, no entanto, ter o cuidado de colocar todos os elementos da fila numa
posicao anterior.
55
5.2 Implementacao de tipos de dados abstratos em Java
• Note-se que no metodo primeiro e necessario, caso nao haja primeiro elemento devolver um resul-
tado que seja entendido como erro para quem programa.
• Note ainda que no caso de nao ter mantido o traco sobre o ultimo elemento o comportamento do
metodo vazia eventualmente nao sera o desejado.
5.2.2 Implementacao dinamica de Listas
Apresenta-se agora a versao dinamica da implementacao de Filas. A esta implementacao denomina-se
lista ligada.
Ao contrario de outras linguagens de programacao como C, Java nao e uma linguagem desenvolvida
com recurso a apontadores que tenham uma aritmetica utilizavel. Contudo estes sao utilizados impli-
citamente pelo Java, no entanto, por uma questao de simplicidade, nao vamos detalhar como e que o
objetos sao implementados como ponteiros em Java.
A ideia e simples com recurso a nos: sempre que se precisa de um novo elemento, cria-se um novo no e
com a funcionalidade de se manter o traco de se esse elemento aponta para um seguinte ou aponta para
o valor nulo. No ultimo caso, esse sera o primeiro elemento da fila.
A classe que discrimina nos pode ser vista por:
1 class Node
2 int val;
3 Node next;
4
5 Node(int v)
6 val=v;
7 next=null;
8
9
Note-se que a classe Node deve ser entendida como um objeto com dois dados: o valor e um ”aponta-
dor”para o no que se lhe segue na Fila.
56
Capıtulo 5. Tipos de dados abstratos e interfaces em Java
A implementacao dinamica e expressa pelo seguinte codigo:
1 public class Filadyn implements Fila
2
3 private Node last;
4
5 public void insere(int x)
6 Node n=new Node(x);
7 n.next=last;
8 last=n;
9
10
11 public int primeiro ()
12 if (last==null) return -1;
13 Node aux=last;
14 while (aux.next!=null) aux=aux.next;
15 return aux.val;
16
17
18 public void retira ()
19 if (last!=null) last=last.next;
20
21
22 public boolean vazia()
23 return last==null;
24
25
26
Deixa-se ao cuidado do leitor a interpretacao deste codigo a semelhanca do que foi feito para a versao
estatica da implementacao. Na proxima aula analisar-se-a com mais detalhe a implementacao quando
forem estudadas as nocoes de heranca e polimorfismo.
57
AULA
6Nocoes de Heranca e Polimorfismo
• Heranca;
• Polimorfismo;
• Exemplos praticos;
59
6.1 Heranca
SECCAO 6.1
Heranca
Dado que Java e uma linguagem em que ha a necessidade de se utilizar varias classes torna-se muito
util, em vez de se definirem classes diferentes para designar objetos que tem caracterısticas comuns,
cria-se uma nova classe com todas essas caracterısticas novas, ”herdando”caracterısticas de um objeto
ou classe ja existente. Posto de outra forma, a heranca e, na verdade, uma classe derivada de outra
classe.
Uma particularidade fundamental do Java e permitir que uma classe herde apenas caracterısticas de
uma unica classe, isto e, nao se pode dar a uma classe, por heranca, propriedades que duas ou mais
classes diferentes se estas forem classes distintas. Apenas pode herdar propriedades de varias classes se
for em cadeias, ou seja, pode herdar de duas desde que uma seja ja subclasse da outra.
No que se segue explica-se atraves de exemplos o uso desta valencia do Java. Um exmplo ilustrativo e
o seguinte:
Exemplo 6.1
Consideremos uma classe de objetos designada por Eletrodomestico onde estao definidos atributos
como ligado (boolean), voltagem (int) e consumo (int). etc...
Dentro dos eletrodomestico podemos definir uma classe de computadores, que para alem de serem
eletrodomesticos tem outros atributos particulares como por exemplo, processador (long), etc.
De entre os computadores ainda se pode especificar mais definindo por exemplo a subclasse de
portateis onde se incluem caracterısticas como peso(long), tamanhoecra(long)...
De modo a se demonstrar os conceitos anteriores a funcionar considera-se de novo o exemplo da aula
anterior com a implementacao em Java de uma classe Lista.
Exemplo 6.2
Considere a situacao de ter acesso a classe Lista (compilada e nao ao codigo fonte) a qual se quer
acrescentar o metodo comprimento:
int comp ( )
e redefinir o metodo retira que em vez de retirar o primeiro elemento, retira o ultimo.
Para tal cria-se a seguinte (sub)classe.
60
Capıtulo 6. Nocoes de Heranca e Polimorfismo
1 public class Filadext extends Filadyn
2
3 protected int comp;
4
5 public Filadext ()
6 comp =0;
7
8
9 public int comp()
10 return comp;
11
12
13 public void insere(int i)
14 super.insere(i);
15 comp ++;
16
17
18 public void retira ()
19 if(last!=null && first!=last)
20 Node aux=first;
21 while(aux.next!=last) aux=aux.next;
22 last=aux;
23 last.next=null;
24 comp --;
25
26 else if(first!=null && first==last)
27 first=null;
28 last=null;
29 comp --;
30
31
32
Note-se que e necessario redefinir o metodo insere uma vez que de cada vez que se insere um novo
elemento e necessario incrementar o comprimento da fila. Dado que a unica alteracao ao codigo do
61
6.2 Polimorfismo
metodo insere e acrescentar a instrucao comp++, basta fazer uma chamada da funcao insere da super
classe Fila mas para que o Java saiba qual das funcoes usar, usa-se o comando super para fazer
referencia a funcao da classe original.
Como estamos tratando de heranca de classes, toda classe tem seu metodo construtor. Portanto, se
estamos trabalhando com duas classes, temos dois metodos construtores. Para se aceder ao metodo
construtor da classe que esta sendo herdada usa-se o comando super.
Podemos usar o comando super para qualquer construtor da classe pai, pois o Java consegue diferenciar
os construtores por causa da sobrecarga de metodos. Note que uma particularidade do Java com a qual
deve ter cuidado e que se usar dois metodos de interfaces diferentes mas com o mesmo nome e com
tipos diferentes de resultados o compilador dar erro.
SECCAO 6.2
Polimorfismo
Considere-se o seguinte codigo:
1 public class Main
2
3 public static void print(Fila f)
4 System.out.println(f.primeiro ());
5 f.retira ();
6 System.out.println(f.primeiro ());
7 f.retira ();
8 System.out.println(f.primeiro ());
9
10
11 public static void main(String [] args)
12 Fila f=new Filadext (),g=new Filadyn ();
13 Filadyn h;
14 f.insere (1);
15 f.insere (3);
62
Capıtulo 6. Nocoes de Heranca e Polimorfismo
16 f.insere (7);
17 System.out.println ((( Filadext) f).comp);
18 print(f);
19
20
Note que quando se implementa a funcao print o compilador nao sabe ao certo que funcao retira deve
utilizar dado que depende do tipo de Fila que vai receber. Se for do tipo Filadyn vai usar o metodo
retira da subclasse e se for do tipo Fila usara o metodo da classe original. Este comportamento e
um exemplo claro do que se entende por polimorfismo, ou seja, dependendo do tipo de dados, a funcao
comporta-se de forma diferente. Note-se que o Java usa o metodo retira o mais abaixo na hierarquia
de classes possıvel. Experimente chamar a funcao print com a fila h e interprete os resultados.
Ao se definir f como do tipo Fila (ainda que depois seja mais especificamente do tipo dinamico) o
Java interpreta que o metodo comp nao esta disponıvel para f . Se se souber que realmente f e do tipo
dinamico pode-se aceder ao metodo comp, exigindo que f se transforme em fila dinamica, operacao que
se designa por cast atraves do comando ((Filadext) f).comp.
63
AULA
7Pesquisa em Tabelas
Temas abordados
• Tipos de dados de Tabela;
• Problema da pesquisa em tabelas;
• Analise de complexidade da pesquisa
– Nocao de funcoes geradoras;
– Uso de funcoes geradoras no calculo da complexidade;
Parte do conteudo desta aula e material que e parte integrante das sebentas de Matematica Discreta de
P. Mateus e C. Sernadas. Para um entendimento completo das tematicas que aqui se abordam sugere-se,
como literatura complementar as referidas notas [?].
65
7.1 Pesquisa em Tabelas
SECCAO 7.1
Pesquisa em Tabelas
Funcionalmente o tipo de dados Tabela e parecido com o tipo de dado Fila que foi visto numa das
ultimas aulas. Pode-se genericamente ver um tipo de dados Tabela com os seguintes metodos:
• pesquisa: que dada uma tabela e um inteiro retorna um booleano de acordo com a ocorrencia
desse inteiro na tabela.
• retira: que dada uma tabela e um inteiro devolve uma tabela sem o dado inteiro caso ele exista.
• vazia: que retorna verdadeiro se a tabela for vazia e falso caso contrario.
• insere: que dada uma tabela e um inteiro devolve uma tabela com o dado inteiro inserido.
A titulo de exemplo refere-se a specificacao equacional para o pesquisa e retira:
• pesquisa(nova, X) = f alse
• pesquisa(insere(W, X), Y) = (X == Y||pesquisa(W, Y))
• retira(nova, X) = retira
• retira(insere(W, X), X) = W;
• retira(insere(W, X), Y) = insere(retira(W, Y)) se X! = Y
A interface deste tipo de dados abstrato e descrita em Java pelo seguinte codigo:
1 public interface Tabela
2
3 public void insere(int i);
4 public void retira(int i);
5 public boolean vazia();
6 public boolean pesquisa(int i);
7
66
Capıtulo 7. Pesquisa em Tabelas
SECCAO 7.2
Implementacao em listas ligadas
Especifica-se agora a forma de como, em Java, se implementa a classe Tabela como sendo uma extensao
da classe fila dinamica:
1 public class FilaP extends Filadyn implements Tabela
2
3 public boolean pesquisa(int i)
4 Node aux=first;
5 while(aux!=null && aux.val!=i) aux=aux.next;
6 return aux!=null;
7
8
9 public void retira(int i)
10 Node aux=first;
11 if (aux==null) return;
12 if (aux.val==i)
13 retira ();
14 return;
15
16 while(aux.next!=null && aux.next.val!=i) aux=aux.next;
17 if(aux.next!=null) aux.next=aux.next.next;
18
19
A analise da complexidade de algoritmos de pesquisa e usualmente feita consideram-se os seguintes
dois casos:
• O caso em que a pesquisa e bem sucedida, isto e quando o elemento e encontrado;
• O caso em que a pesquisa falha, ou seja, o elemento nao pertence a lista;
Seja n o comprimento da fila. No caso de sucesso, a operacao pesquisa demora:
67
7.3 Tabelas de dispersao
• No pior caso: O(n), dado que no pior caso ter-se-a de comparar todos os elementos;
• No caso medio: O
(n
∑k=1
kn
)= O
(n2
)= O(n), admitindo que a lista esta ordenada. Note-se que
se ha sucesso na pesquisa, entao sao feitas, para cada inteiro i, no maximo i comparacoes.
• No melhor caso: O(1), quando o elemento em causa e logo o primeiro elemento da lista.
Um majorante para a complexidade no caso de insucesso do metodo de pesquisa e O(n). (O leitor deve
entender o porque desta afirmacao e indagar o porque da sua veracidade!) Contudo a questao que o
leitor pode fazer e se, no caso de nao estar na lista o elemento, se se podera fazer melhor? Outra questao
que o leitor deve colocar-se a si mesmo e: “Qual a relevancia deste problema?”
Certamente que depois de alguma reflexao o leitor obteve uma resposta para a ultima questao. Dado
que a pesquisa e a operacao mais efectuada com listas, o valor O(n), onde n e o numero de registo
e inaceitavel especialmente em listas com milhoes de registos. A resposta a primeira questao sera
respondida nas seccoes que se seguem.
SECCAO 7.3
Tabelas de dispersao
Uma da tecnicas para melhorar a pesquisa de uma tabela de registo e chamada tecnica de dispersao
aleatoria (em Ingles “hashing”). A ideia consiste em dividir o espaco de armazenamento de registos
em m tabelas e considerar uma funcao de dispersao h que transforma cada chave possıvel no ındice da
tabela a procurar a chave k, ou seja, num valor
h(k) ∈ 0, ..., m− 1.
Desta forma o registo com chave k fica guardado na tabela de ındice k.
Note-se que uma tabela de dispersao tem a mesma assinatura de uma tabela descrita ulteriormente, mas
requer uma funcao de dispersao h. O codigo em Java de implementacao desta classe e:
68
Capıtulo 7. Pesquisa em Tabelas
1 public class TabelaDisp implements Tabela
2 FilaP [] disp;
3 private int hash(int i)
4 return i%disp.length;
5
6 TabelaDisp(int dim)
7 disp=new FilaP[dim];
8 int j=0;
9 while(j<dim)
10 disp[j]=new FilaP();
11 j++;
12
13
14
15 public void insere(int i)
16 disp[hash(i)]. insere(i);
17
18
19 public boolean vazia()
20 int j=0;
21 while(j<disp.length)
22 if(!disp[j].vazia ()) return false;
23 j++;
24
25 return true;
26
27
28 public void retira(int i)
29 disp[hash(i)]. retira(i);
30
31
32 public boolean pesquisa(int i)
33 return disp[hash(i)]. pesquisa(i);
34
35
69
7.4 Funcao geradora
Mas a verdadeira questao mantem-se! O que se ganhou com este processo? No pior caso de insucesso
continua-se a ter O(n). Mas como se justifica mais a diante, o ganho e no caso medio. Embora se possa
efetuar a analise sem recorrer a funcoes geradoras, opta-se por introduzir desde ja esta tecnica com uma
ferramenta muito util para o estudo de complexidade.
SECCAO 7.4
Funcao geradora
As funcoes geradoras sao um instrumento fundamental em probabilidades. Uma funcao geradora de
uma variavel aleatoria e o valor esperado de uma certa transformacao da variavel aleatoria da qual
se podem caracterizar e inferir momentos da propria variavel aleatoria num raio de convergencia.
Sob certas condicoes, a funcao geradora de momentos determina completamente a distribuicao de
probabilidade.
No que se segue apresentam-se as definicoes rigorosas de funcao geradora de sucessao de numeros reais
e de um uma variavel aleatoria, destacando algumas das suas propriedades relevantes para o estudos
da complexidade supra referido.
Definicao 7.1
Dada uma sucessao de numeros reais s = snn∈N , a funcao geradora associada a s e definida como
G(z) =∞
∑i=0
sizi.
Tal como se expressou atras, as funcoes geradoras tem propriedades uteis. Dessas propriedades destacam-
se as que se enumeram na proposicao seguinte e que se serao uteis no seguimento desta aula.
Proposicao 7.1
• zmG(z) =∞
∑i=0
si−mzi onde s−k = 0 para todo k ≥ 0;
• G(c · z) =∞
∑i=0
cisizi;
70
Capıtulo 7. Pesquisa em Tabelas
• G′(z) =∞
∑i=0
(i + 1)si+1zi;
•∫ z
0G(z)dz =
∞
∑i=1
1i
si−1zi
Seja X uma variavel aleatoria que toma valores inteiros nao negativos, podemos associar a variavel uma
sucessao de probabilidades como se segue: (sX)
n= PX(n).
Neste contexto, podemos definir a funcao geradora de probabilidade de (sX) como
GX(z) =∞
∑k=0
PX(k)zk.
A funcao gerada inclui toda a informacao sobre a variavel aleatoria X.
Alem disso, a funcao geradora de uma soma de variaveis aleatorias independentes e o produto das
funcoes geradoras de cada componente da soma, ou seja, temos ainda que G∑ Xi(z) = ∏ GXi(z) se Xi e
independente de Xj para i 6= j.
Os momentos de uma variavel aleatoria podem ser obtidos pela derivacao da funcao geradora.
Proposicao 7.2
Dada uma variavel aleatoria X que toma valores inteiros nao negativos,
• E(X) = G′X(1);
• E(Xn) =∞
∑k=0
knPX(k);
• E(X2) = G′′X(1) + G′X(1);
• Var(X) = G′′X(1) + G′X(1)−(G′X(1)
)2.
Teorema 7.1
Sejam X e Y duas variaveis aleatorias. Tem-se que:
GX = ∑y∈Y
PY(Y = y)GX|y.
71
7.4 Funcao geradora
As provas destas propriedades podem ser consultadas em qualquer livro sobre probabilidades e es-
tatıstica e em especial em [?].
A convergencia ordinaria de uma sequencia de funcoes geradoras corresponde a convergencia das
correspondentes distribuicoes.
7.4.1 Caso de insucesso da pesquisa em tabelas
Considere-se a situacao de m listas e n chaves. Cada chave pode estar inserida numa das m listas. Logo
ha mn possibilidades para a insercao das n chaves. Como exemplo ilustrativo, suponha que se tem duas
listas L1 e L2 e duas chaves K1 e K2. As configuracoes possıveis sao:
• K1 ∈ L1 e K2 ∈ L1;
• K1 ∈ L1 e K2 ∈ L2;
• K1 ∈ L2 e K2 ∈ L1;
• K1 ∈ L1 e K2 ∈ L2.
Suponha ainda que lhe e dada uma nova chave K3. Se a chave nao se encontrar nas listas, entao, dado
que K3 pode ocorrem numa das duas listas entao para a contagem do numero de comparacoes tem de
se considerar todos os 8 casos existentes:
• K1, K2, K3 ∈ L1 - Neste caso sao necessarias duas comparacoes;
• K1, K2 ∈ L1 e K3 ∈ L2 - Neste caso nao sao necessarias comparacoes;
• K1, K3 ∈ L1 e K2 ∈ L2 - Neste caso e preciso uma comparacao;
• K1 ∈ L1 e K2, K3 ∈ L2 - Igualmente uma comparacao;
• K1, K3 ∈ L2 e K2 ∈ L1 - Igualmente uma comparacao;
• K1 ∈ L2 e K2, K3 ∈ L1 - Igualmente uma comparacao;
• K3 ∈ L1 e K1, K2 ∈ L2 - Neste caso nao sao necessarias comparacoes;
• K1, K2, K3 ∈ L2 - Este ultimo caso requer duas comparacoes;
72
Capıtulo 7. Pesquisa em Tabelas
Considere-se as variaveis aleatorias Xi independentes com i ∈ 1, ..., n, tomando valores em 0, 1 defi-
nida por Xi = 1 se e so se h(Ki) = h(K3). Assuma que todas as listas sao igualmente provaveis, ou seja
que
P(Xi = 1) =1m
.
Claramente, o numero total de comparacoes e a variavel aleatoria
NC =n
∑i=1
Xi.
Pretende-se entao calcular o valor medio e a variancia deste numero de comparacoes. Para isso calcula-
se a funcao geradora Gi de Xi:
Gi(z) =1
∑j=0
P (Xi = j)× zj
=(m− 1)
m× z0 +
1m× z1
=(m− 1 + z)
m
Logo a funcao geradora G de NC e
G(z) =((m− 1 + z)
m
)n
e portanto
E(NC) = G′(1) =nm
e tambemVar(NC) = G′′(1) + G′(1)−
(G′(1)
)2
=(−1 + m)n
m2 .
7.4.2 Caso de sucesso da pesquisa em tabelas
Considere-se novamente a situacao de m listas e n chaves. No caso de sucesso, naturalmente a chave a
encontrar pode estar entre 1 e n. Tomando o exemplo ilustrativo anterior, suponha-se que temos duas
listas L1 e L2 e tres chaves K1, K2 e K3 e as listas encontram-se na seguinte configuracao
K1, K2 ∈ L1, K3 ∈ L2.
73
7.4 Funcao geradora
O numero de comparacoes necessario para encontrar a chave e:
• K1 → 1 comparacao;
• K2 → 2 comparacoes;
• K3 → 1 comparacao.
Seja Y a variavel aleatoria tal que Y = j sse a chave a encontrar e Kj. Consideremos as variaveis aleatorias
Xi independentes com i ∈ 1, ..., n, tomando valores em 0, 1, mais precisamente, tomando valor 1
sse h(Ki) e igual ao valor de h calculado sobre Y-esima chave. O numero de comparacoes NCj dado que
a chave a encontrar e Kj e tal que:
NCj =j
∑i=1
Xi
Observe que P(Xj = 1|Y = j
)= 1. Logo
GNCj(z) =j
∏i=1
Gi|j(z)
=j
∏i=1
(1
∑k=0
P (Xi = k|Y = j)× zk
)
= (j−1
∏i=1
(1
∑k=0
P (Xi = k|Y = j)× zk)
)× z
=
(j−1
∏i=1
Gi(z)
)× z
=
(j−1
∏i=1
(m− 1 + z)m
)× z
=
((m− 1 + z)
m
)j−1
z
74
Capıtulo 7. Pesquisa em Tabelas
Portanto como P(NC = x) =n
∑j=1
P(Y = j)P(NCj = x) e logo
GNC =n
∑j=1
P(Y = j)GNCj
=n
∑j=1
GNCj
n
=∑n
j=1
(((m−1+z)
m
)j−1z)
n
=m× z×
((m−1+zm
)n − 1)
n(z− 1)
e portanto, conclui-se que
E(NC) =−1 + 2m + n
2m
e que
Var(NC) =(−1 + n)(−5 + 6m + n)
12m2 .
Observe que se n = O(m) no caso medio temos que a pesquisa e O(1)!
No entanto chama-se a atencao que a eficiencia do uso de funcoes de dispersao esta intimamente ligada
a escolha da funcao de dispersao.
SECCAO 7.5
Escolha da funcao de dispersao
A funcao de dispersao deve ser tal que |h−1(y)| ≈ nm
para todo o y. No entanto observe que mesmo
nesta situacao nada impede que seja sempre escolhidos (por algum adversario) registos que vao parar
a mesma tabela de dispersao. Para evitar este cenario considera-se que a funcao de dispersao deve ser
escolhida de uma famıliaH dita universal.
75
7.5 Escolha da funcao de dispersao
Definicao 7.2
Uma colecao de funcoes de dispersao H e dita universal se para cada par de chaves distintas k1, k2,
o numero de funcoes de dispersao em h ∈ H, tal que h(x) = h(y) e precisamenteHm
.
Teorema 7.2
Se h for escolhido de uma colecao de funcoes de dispersao universais e h for usada para dispersar
n chaves numa tabela de tamanho m com n ≤ m, entao o valor esperado para o numero de colisoes
envolvendo uma chave k e menor que 1.
Demonstracao : Seja Ck,y a variavel aleatoria que toma o valor 1 quando h(k) = h(y) para um h escolhido
aleatoriamente em H. Entao E(Ck,y) =1m
por definicao de universalidade de H. Assim E(Ck) = E(∑y 6=k
Ck,y) =
∑y 6=k
E(Ck,y) =n− 1
m.
Exemplo 7.3
Colecao universal de funcoes de dispersao:
• Assuma que as chaves tem um tamanho maximo e que uma chave pode ser decomposta em
r + 1 partes
k = (k0, . . . , kr)
onde o valor maximo de cada parte e menor que um primo m.
• Seja a = (a0, . . . , am) ∈ Zr+1m , isto e, aj ∈ Zm = 0, . . . , m− 1.
• ha(x) =r
∑i=0
aiki mod m;
• H = ha : a ∈ Zr+1m . e tem mr+1 elementos.
Teorema 7.3
A classe de funcoes de dispersaoH definida no Exemplo 6 e universal.
Demonstracao : Sejam k = (k0, . . . , kr) e y = (y0, . . . , yr) chaves distintas. Logo existe uma parte j tal que k j 6= yj.
Entao, ha(x) = ha(y) se e so se
aj(k j − yj) = −∑i 6=j
ai(xi − yi) (mod m).
Para cada possıvel valor dos ai’s existe uma unica solucao para aj (por m ser primo), logo ha mr =mr+1
m.
76
AULA
8Arvores binarias de pesquisa
Temas abordados
• Arvores binarias;
• Pesquisa em arvores binarias;
77
8.1 Arvores binarias
SECCAO 8.1
Arvores binarias
Considere que se enriquece o conjunto de operacoes sobre Java com o tipo arvore binaria de inteiros
cuja assinatura e:
• ArvBin:→ arv, que e uma funcao construtora da arvore inicialmente vazia.
• ArvBinDyn:int× arv×arv → arv que dada uma nova raiz, e duas arvores constroi uma nova
arvore binaria.
• maior:arv→int que dada uma arvore devolve o maior valor dos nos da arvore.
• maior:arv→int que dada uma arvore devolve o menor valor dos nos da arvore.
• prof:arv→int que devolve a profundidade maxima da arvore.
• pesquisa:int→boolean que dado um inteiro pesquisa se ele ocorre na arvore;
• insere:int→arv que insere um inteiro na arvore;
• retira:int→arv que retira um inteiro da arvore caso ele ocorra;
• vazia:arv→boolean que verifica se a arvore e vazia.
1 public interface ArvBin
2 public ArvBinDyn ();
3 public ArvBinDyn(int i,ArvBin l,ArvBin r);
4 public int maior ();
5 public int menor ();
6 public int prof();
7 public void insere(int i);
8 public void retira(int i);
9 public boolean pesquisa(int i);
10
78
Capıtulo 8. Arvores binarias de pesquisa
A especificacao dos metodos supra referidos e expressa pelo codigo que se apresenta de seguida. Comeca-
se por definir a classe BinTNode que permite criar, partindo de elementos base para criar uma Arvore
binaria, uma nova Arvore binaria com esses elementos. Assim, dado um valor chave e duas arvores
binarias constroi-se um novo no (que no fundo e uma estrutura de arvore binaria) que especifica que a
raiz dessa estrutura e o valor chave e os ramos que dela derivam sao as duas arvores dadas.
1 class BinTNode
2 ArvBin ltree ,rtree;
3 int key;
4 BinTNode(int i, ArvBin l,ArvBin r)
5 key=i;
6 ltree=l;
7 rtree=r;
8
9
O codigo que implementa entao a interface descrita acima e:
1 public class ArvBinDyn implements ArvBin , Tabela
2 BinTNode root;
3
4 public ArvBinDyn ()
5 root=null;
6
7
8 public ArvBinDyn(int i,ArvBin l,ArvBin r)
9 root=new BinTNode(i,l,r);
10
11
12 public int maior ()
13 if(root!=null)
14 int rmax=root.key ,lmax=root.key;
15 if(root.rtree !=null) rmax=root.rtree.maior();
79
8.1 Arvores binarias
16 if(root.ltree !=null) lmax=root.ltree.maior();
17 return Math.max(Math.max(rmax , lmax),root.key);
18
19 return -1;
20
21
22 public int menor ()
23 if(root!=null)
24 int rmin=root.key ,lmin=root.key;
25 if(root.rtree !=null) rmin=root.rtree.menor();
26 if(root.ltree !=null) lmin=root.ltree.menor();
27 return Math.min(Math.min(rmin , lmin),root.key);
28
29 return -1;
30
31
32 public int prof()
33 if(root!=null)
34 int rprof=0,lprof =0;
35 if(root.rtree !=null) rprof=root.rtree.prof();
36 if(root.ltree !=null) lprof=root.ltree.prof();
37 return 1+Math.max(rprof , lprof);
38
39 return 0;
40
41
42 public boolean pesquisa(int i)
43 if(root==null) return false;
44 if (root.key==i) return true;
45 if(root.rtree!=null && root.rtree.pesquisa(i)) return
true;
46 if(root.ltree!=null && root.ltree.pesquisa(i)) return
true;
47 return false;
48
49
80
Capıtulo 8. Arvores binarias de pesquisa
50 public void insere(int i)
51 if(root==null) root=new BinTNode(i,null ,null);
52 else if(root.rtree==null) root.rtree=new ArvBinDyn(i,null
,null);
53 else if(root.ltree==null) root.ltree=new ArvBinDyn(i,null
,null);
54 else
55 root= new BinTNode(i,new ArvBinDyn(root.key ,root.
ltree ,root.rtree),null);
56
57
58
59 public void retira(int i)
60 if(i==root.key)
61 if(root.rtree ==null) root =(( ArvBinDyn)root.ltree)
.root;
62 else if(root.ltree==null) root =(( ArvBinDyn)root.
rtree).root;
63 else
64 root.key =(( ArvBinDyn)root.rtree).root.key
;
65 root.rtree.retira(root.key);
66
67 else if(root.rtree!=null && root.rtree.pesquisa(i))
root.rtree.retira(i);
68 else if (root.ltree!=null && root.ltree.pesquisa(i)) root
.ltree.retira(i);
69
70
71 public boolean vazia()
72 return root==null;
73
74
81
8.2 Arvores binarias de pesquisa
SECCAO 8.2
Arvores binarias de pesquisa
Um tipo de especial interesse de arvores binarias sao as arvores binarias de pesquisa. Este tipo de
estruturas tem a particularidade de todos os nos da subarvore esquerda terem um valor numerico
inferior ao no raiz e todos os nos da subarvore direita terem um valor superior ao no raiz. O objetivo
destas arvores e estruturar os dados de forma flexıvel, permitindo pesquisa binaria.
8
3 10
1 6 14
4 7 13
Figura 8.1: Exemplo de uma arvore de pesquisa binaria.
O metodo de verificacao se uma dada arvore binaria e uma arvore binaria de pesquisa pode ser expressa
pela implementacao do seguinte metodo pesqQ acrescentado a classe ArvBinDyn.
1 public boolean pesqQ()
2 if(root==null) return true;
3 if(root.rtree!=null && (root.rtree.menor()<root.key ||! root.rtree.
pesqQ ())) return false;
4 if(root.ltree!=null && (root.ltree.maior()>root.key ||! root.ltree.
pesqQ ())) return false;
5 return true;
6
82
Capıtulo 8. Arvores binarias de pesquisa
Note-se que neste tipo de estrutura os seus metodos nao podem ser bem iguais aos das arvores binarias
em geral. De facto para preservarem a estrutura de arvore binaria de pesquisa, por exemplo e necessario
que o metodo insere localize (atraves de comparacoes) o melhor sıtio para inserir o elemento. Assim, o
metodo insere numa arvore nao vazia de um elemento que nao ocorre na arvore ocorre colocando,
por exemplo o numero num novo no criado numa folha a esquerda ou a direita conforme o valor
de referencia do no encontrado. Eventualmente, alcanca-se a folha, inserindo-se entao o valor nesta
posicao. Ou seja, a raiz e examinada e introduz-se um no novo na subarvore da esquerda se o valor
novo for menor do que a raiz, ou na subarvore da direita se o valor novo for maior do que a raiz.
Da mesma forma, no metodo retira tem de se ter em linha de conta que retirar apenas pode comprome-
ter a estrutura de arvore binaria de pesquisa. No exemplos que se seguem ilustram-se alguns possıveis
casos.
Exemplo 8.1 (de funcionamento do metodo retira )
Retirar uma folha: basta remove-lo da arvore.
Retirar um no com um filho: o filho sobe para a posicao do pai.
Retirar um no com dois filhos: pode-se faze-lo de duas formas diferentes. Uma substituindo o
valor do no a ser retirado pelo valor sucessor (o no mais a esquerda da subarvore direita). A
outra atraves do valor antecessor (o no mais a direita da subarvore esquerda), removendo-se
aı o no sucessor (ou antecessor). De modo geral tem-se:
83
8.2 Arvores binarias de pesquisa
Neste exemplo para retirar o no com valor 30 e dado que possui como sucessor imediato o
valor 35 (no mais a esquerda da sua subarvore direita, este ultimo e promovido ao lugar do no
a ser excluıdo, enquanto a sua subarvore (direita) sera promovida para sub-arvore esquerda
do 40.
Teorema 8.1
Seja T uma arvore binaria de pesquisa com profundidade h. Pesquisar um elemento, encontrar o
maximo e o mınimo em T pode ser feito em O(h).
Demonstracao : Considere o seguinte algoritmo de pesquisa
1 public class ArvBinPesq extends ArvBinDyn implements Tabela
2
3 public ArvBinPesq(int i, ArvBinPesq l, ArvBinPesq r)
4
5
84
Capıtulo 8. Arvores binarias de pesquisa
6
7 public void insere(int i)
8 if(root==null)
9 root=new BinTNode(i,null ,null);
10 else
11 if(i==root.key) return;
12 if(i>root.key)
13 if(root.rtree!=null)root.rtree.insere(i);
14 else root.rtree=new ArvBinPesq(i,null ,
null);
15 else
16 if(root.ltree!=null) root.ltree.insere(i)
;
17 else root.ltree=new ArvBinPesq(i,null ,
null);
18
19
20
21
22 public void retira(int i)
23 if (root==null) return;
24 if (root.key==i)
25 if(root.ltree ==null&&root.rtree==null) root=null;
26 else if (root.ltree!=null)
27 root.key=root.ltree.maior();
28 (( ArvBinPesq) root.ltree).retira(root.key
);
29 else
30 root.key=root.rtree.menor();
31 (( ArvBinPesq) root.rtree).retira(root.key
);
32
33 else if(root.key <i) root.rtree.retira(i);
34 else root.ltree.retira(i);
35
36
85
8.2 Arvores binarias de pesquisa
37 public boolean pesquisa(int i)
38 if(root==null) return false;
39 if(root.key==i) return true;
40 if(root.key <i) return root.rtree.pesquisa(i);
41 return root.ltree.pesquisa(i);
42
43
44 public int maior ()
45 if(root==null) return -1;
46 if(root.rtree==null) return root.key;
47 return root.rtree.maior();
48
49
50 public int menor ()
51 if(root==null) return -1;
52 if(root.ltree==null) return root.key;
53 return root.ltree.menor();
54
55
O algoritmo esta correto. Deixa-se ao cuidado do leitor encontrar e provar um invariante. Note-se que como em
cada passo do ciclo a arvore e alterada para um descendente, o numero maximo de iteradas que e necessario para
completar o ciclo e O(h).
As arvores de pesquisa podem implementar uma tabela de registos com as operacoes de insere!
Deixa-se novamente ao cuidado do leitor encontrar do ciclo da funcao insere! Deixa-se ainda ao cuidado
do leitor calcular a complexidade da operacao de insere e repetir estas mesmas questoes para o operacao
de apaga referida nas especificacoes da funcao.
Teorema 8.2
A profundidade media de uma arvore de pesquisa cujos os elementos k1, . . . kn foram inseridos por
um ordem aleatoria e O(log(n)).
86
AULA
9B-trees
Temas abordados
• Complexidade da pesquisa em arvores binarias;
• B - trees;
87
9.1 Complexidade da insercao aleatoria em arvores binarias de pesquisa
SECCAO 9.1
Complexidade da insercao aleatoria em arvores binarias de pesquisa
Recorde o conceito de arvore binaria de pesquisa dado na aula anterior e relembre o enunciado do
seguinte teorema que ficou por demonstrar. Para um estudo mais detalhado do tema abordado nesta
seccao e na seccao anterior, sugere-se a leitura do Capıtulo 12 do livro de texto [CSRL01].
Teorema 9.1
A profundidade media de um no numa arvore de pesquisa cujos os elementos k1, . . . kn foram inse-
ridos por um ordem aleatoria e O(log(n)).
Demonstracao : Seja d(x, T) a profundidade de cada no x numa arvore binaria e assuma tambem que a raiz tem
profundidade zero. Seja P(T) = ∑x
d(x, T) a soma das profundidades de todos os nos.
Fixada a arvore T a profundidade media de um no, escolhido de forma uniforme, e
E[d(x, T)] = ∑x
1n
d(x, T) =P(T)
n.
Se Tl e Tr forem respetivamente as sub-arvores esquerda e direita e facil inferir a seguinte recorrencia:
P(T) = P(Tl) + P(Tr) + n− 1.
Note-se que o peso da arvore e dado pelo peso das duas arvores que a compoe e a todos os nos de Tl e Tr (que sao
n− 1, todos exceto a raiz) acrescem uma unidade na profundidade em T.
Seja P(n) = E[P(T)] e |T| = n o numero de nos em T. Se os elementos k1, . . . kn forem inseridos de forma aleatoria
em |Tl | = i e |Tr| = n− 1− i com probabilidade1n
para todo o i = 0...n− 1 entao
P(n) =1n
(n−1
∑i=0
P(i) + P(n− i− 1) + n− 1
)
=2n
(n−1
∑i=1
P(i)
)+ Θ(n)
Esta recorrencia e semelhante a recorrencia que ja resolvemos para o quicksort e a solucao e
P(n) = O(n log n)
e portanto, podemos concluir que
E[d(x, T)] =P(n)
n= O(log n).
88
Capıtulo 9. B-trees
Apesar da insercao ser feita em tempo logarıtmico existem alguns detalhes que inviabilizam este re-
sultado. m muitos casos os dados nao sao inseridos aleatoriamente, isto e, sao inseridos com um certo
enviesamento. Na verdade, em situacoes praticas de utilizacao destas estruturas tipicamente os dados
sao inseridos quase de forma ordenadas (novas contas, novos numeros de ID etc). Assim sendo, nestes
casos as arvores nao ficam com nos com profundidade media O(log(n)) como seria de esperar. Coloca-
se entao a questao pertinente ao leitor: “Como se pode resolver estes problemas de modo a obter uma
estrutura em que as operacoes descritas na ultima aula posso ser realizadas em tempo logarıtmico?” A
resposta e o uso de B-trees.
SECCAO 9.2
B - trees
As B - tree sao uma estrutura de dados muito utilizada em aplicacoes que lidam e manipulam grandes
quantidades de informacao dado que que estas estruturas possibilitam que a insercao, remocao e pes-
quisa de chaves seja feita em tempo logarıtmico no tamanho dos dados. A sua invencao remonta a 1971
e foi apresentada por Rudolf Bayer e Edward McCreight investigadores, a data, da Boeing Scientific
Research Labs. Pensa-se que a origem do nome B-tree ainda que possa nao ter sido proposto pelos seus
criadores, tenha a haver com balanceamento, ou com o nome de um de seus inventores Bayer ou entao
com a propria Boeing. As B-trees sao uma generalizacao das arvores binaria de pesquisa, pois cada no
de uma arvore binaria armazena uma unica chave, enquanto as B-tree armazenam mais que uma chaves
de pesquisa em cada no.
Comeca-se por definir rigorosamente o que se entende por uma 2− 3tree.
Definicao 9.1
Uma 2-3 tree e uma arvore balanceada em que:
• Todos os nos tem sub-arvores exatamente com o mesma profundidade;
• Todos os nos sao de uma das seguintes formas:
– 2-nos: contem uma chave e dois descendentes;
– 3-nos: contem duas chaves e tres descendentes;
• Os elementos da arvore estao ordenados de modo a que:
89
9.2 B - trees
– Num 2-no, os elementos a esquerda sao todos menores que a chave e a direita maiores
que a chave;
– Num 3-no, os elementos a esquerda sao todos menores que a primeira chave, os elementos
ao centro estao entre a primeira e a segunda chave e a direita sao maiores que a segunda
chave;
Na figura que se segue apresenta-se um exemplo de uma 2-3 tree.
A pesquisa e feita de maneira mais ou menos trivial, dependendo do no ser do tipo 2 ou 3. Deixa-se ao
cuidado do leitor os detalhes da pesquisa. Sugere-se que o leitor implemente a pesquisa usando Java.
No que se segue explica-se o processo de insercao de uma chave x nao existente na arvore. O processo
de insercao funciona da seguinte forma:
90
Capıtulo 9. B-trees
1. Pesquisar a folha n onde x deveria estar;
2. Se a folha e um 2-no transforma-se num 3-no com x;
50
Insertion in 2-3 Trees• Algorithm for inserting an item with a key k:
search for k, but don’t stop until you hit a leaf nodelet L be the leaf node at the end of the searchif L is a 2-node
add k to L, making it a 3-node
else if L is a 3-nodesplit L into two 2-nodes containing the items with the
smallest and largest of: k, L’s 1st key, L’s 2nd keythe middle item is “sent up” and inserted in L’s parent
example: add 52
50
54 70… …
50 54
52 70…52 54 70
10
3 20
10
3 14 20
Example 1: Insert 8• Search for 8:
• Add 8 to the leaf node, making it a 3-node:
28 61
10 40
3 34 51
77 90
68 93 9714 20 80 87
28 61
10 40
34 51
77 90
68 93 9714 20 80 873 8
Figura 9.1: Insercao de 14 num 2-no
3. Se a folha e um 3-no entao separa-se em dois 2-nos com o menor e o maior valor e o valor do meio
e enviado para cima
Figura 9.2: Insercao de 52 num 3-no
Eventualmente se o pai e um 3-no, este tambem tem de ser separado, podendo esta propagacao ser feita
ate a raiz:
91
9.2 B - trees
92
Capıtulo 9. B-trees
Figura 9.3: Insercao de 92 num 3-no
Este e o unico caso que leva ao aumento da profundidade da arvore. Deixa-se ao cuidado do leitor
verificar com detalhe que a arvore obtida ainda e da forma pretendida, isto e, e uma 2-3 tree.
93
9.2 B - trees
Afirmam-se, sem prova algumas das propriedades das 2− 3 arvores importantes:
• Uma 2-3 arvore com n chaves tem profundidade ≤ log2(n);
• A pesquisa e a insercao sao feitas em tempo O(log n); (O leitor deve consultar a Seccao 18.2 de
[CSRL01] para entender os detalhes da implementacao e da justificacao da complexidade destas
operacoes.)
• Apagar e complicado mas tambem e O(log n). Este procedimento e explicado na Seccao 18.3 de
[CSRL01].
• Nao funciona bem em HD: disco estao organizados em blocos 4K 8K e os acessos aos discos sao
muito lentos...
Pode-se generalizar a ideia de uma 2-3 tree para uma ordem superior, em que se assume que os nos
podem ter um numero arbitrario de chaves. A definicao geral deste tipo de estruturas e:
Definicao 9.2
Uma B-tree de ordem m e uma arvore balanceada em que:
• Cada no tem no maximo 2m chaves (e 2m + 1 descendentes);
• Cada no tem no mınimo m chaves com excecao da raiz que pode ter apenas 1 chave;
• Os elementos da arvore estao ordenados de modo a que:
– Os elementos da sub-arvore mais a esquerda sao todos menores que a primeira chave e
os elementos da sub-arvore mais a direita sao maiores que a ultima chave;
– Os elementos da i-esima sub-arvore a contar da esquerda tem todos os elementos entre a
i− 1-esima e a i-esima chave;
Apresenta-se na figura seguinte uma B-Tree de ordem 2
94
Capıtulo 9. B-trees
Example: a B-Tree of Order 2
• Order 2: at most 4 data items per node (and at most 5 children)
• The above tree holds the same keys as one of our earlier 2-3 trees, which is shown again below:
• We used the same order of insertion to create both trees: 51, 3, 40, 77, 20, 10, 34, 28, 61, 80, 68, 93, 90, 97, 87, 14
• For extra practice, see if you can reproduce the trees!
20 40 68 90
3 10 14 28 34 93 9751 61 77 80 87
28 61
10 40
3 14 20 34 51
77 90
68 80 87 93 97
Search in B-Trees• Similar to search in a 2-3 tree.
• Example: search for 87
20 40 68 90
3 10 14 28 34 93 9751 61 77 80 87
E oportuno observar que:
• Uma 2-3 tree e uma B-tree de ordem 1;
• Ao escolher-se m o maior possıvel garante-se a minimizacao de acessos ao disco.
Para um estudo mais completo e detalhado do tema abordado nesta seccao sugere-se a leitura do
Capıtulo 18 do livro de texto recomendado como bibliografia principal [CSRL01]. Recomenda-se ainda
ao leitor que implemente em Java a estrutura de uma 2-3 tree...
95
AULA
10Algoritmos em grafos
Temas abordados
• Nocao de grafos;
• Implementacao de grafos;
• Problema de acessibilidade;
• Pesquisa em largura;
• Arvore de Extensao Mınima;
• Planaridade de grafos;
97
10.1 Grafos
SECCAO 10.1
Grafos
Definicao 10.1
Um grafo e um par G = (N, E) onde N e um conjunto de nos e E e um conjunto de subconjuntos de
N com dois elementos, dito o conjunto de arestas.
Um caminho e uma sequencia de arestas e1 . . . ek tais que ei ∩ ei+1 6= ∅.
A nocao de grafo orientado e semlhante a nocao de grafo em que se acrescenta a nocao de aresta uma
orientacao.
Exercıcio 10.1. Considere as operacoes
• novo:int->grafo que recebe um numero de nos e constroi um grafo com esse numero de nos e sem arestas;
• adar:grafo×int×int->grafo que adiciona uma aresta a um grafo.
Especifique estas operacoes equacionalmente.
No restante desta aula apresentam-se implementacoes do seguinte interface:
1 public interface GrafoO
2 public void addaresta(int i, int j);
3 public void retiraaresta(int i, int j);
4 public boolean adjacenteQ(int i, int j);
5 public boolean acessivel(int i, int j);
6 public boolean conexo ();
7 public boolean planar ();
8
Ha duas representacao naturais de grafos em memoria:
• Matrizes de adjacencia que requer O(|N2|) espaco;
• Listas de adjacencia que requer O(|N|+ |E|) espaco.
98
Capıtulo 10. Algoritmos em grafos
A implementacao de grafos como matriz de adjacencia e descrita pelo seguinte codigo Java:
1 public class GOMA implements GrafoO
2 int [][] MA;
3
4 public GOMA(int dim)
5 MA=new int[dim][dim];
6 int i=0,j;
7 while(i<dim)
8 j=0;
9 while(j<dim)
10 MA[i][j]=0;
11 j++;
12
13 i++;
14
15
16
17 public void addaresta(int i, int j)
18 MA[i][j]=1;
19
20
21 public void retiraaresta(int i, int j)
22 MA[i][j]=0;
23
24
25 public boolean adjacenteQ(int i, int j)
26 return MA[i][j]==1;
27
28
29 public boolean conexo ()
30 return false;
31
32
33 public boolean planar ()
34 return false;
99
10.1 Grafos
35
36
37 public static void main(String [] args)
38 GOMA g=new GOMA (3);
39 g.addaresta (1,0);
40 g.addaresta (0,1);
41 g.addaresta (2,1);
42 System.out.println(g.acessivel (0,2));
43
44
Ao longo da aula completar-se-ao os metdos em falta.
Como lista de adjacencia um grafo pode ser descrito baseado na estrutura de filas ja idealizada numa
das aulas anteriores atraves do seguinte codigo:
1 public class GOLA implements GrafoO
2 FilaP LA[];
3
4 public GOLA(int dim)
5 LA=new FilaP[dim];
6 int i=0;
7 while(i<dim)
8 LA[i]=new FilaP ();
9 i++;
10
11
12
13 public void addaresta(int i, int j)
14 LA[i]. insere(j);
15
16
17 public void retiraaresta(int i, int j)
18 LA[i]. retira(j);
100
Capıtulo 10. Algoritmos em grafos
19
20
21 public boolean adjacenteQ(int i, int j)
22 return LA[i]. pesquisa(j);
23
24
25 public boolean acessivel(int i, int j)
26 return false;
27
28
29 public boolean conexo ()
30 return false;
31
32
33 public boolean planar ()
34 return false;
35
36
SECCAO 10.2
Problema da acessibilidade
O problema de acessibilidade em grafos e descrito atraves da seguinte questao:
Dados dois nos, existe um caminho que os contenha?
Os seguintes teoremas de demonstracao facil (e que se deixa ao cuidado do leitor) ajudam a idealizar
uma algoritmo para tratar este problema.
Teorema 10.1
Caso exista um caminho de um no para outro, entao existe um caminho entre este nos com dimensao
menor ou igual a |N| − 1.
101
10.2 Problema da acessibilidade
Teorema 10.2
Seja A a matriz de adjacencia de um grafo e B = An com B = biji,j∈1..n. O numero de caminhos
com comprimento n de i para j e precisamente igual a bij.
Exercıcio 10.2. Especifique equacionalmente a funcao acessivelQ:int×int:->bool.
Utilizando os resultados anteriores e a implementacao do tipo grafo em matrizes de adjacencia apresenta-
se uma resolucao do problema da acessibilidade.
1 private static int proxavisitar(int[] v)
2 int i=v.length -1;
3 while(i>=0 && v[i]!=1)
4 i--;
5
6 return i;
7
8
9 public boolean acessivel(int i, int j)
10 int aux ,next ,v[]= new int[MA.length ];
11 v[i]=1;
12 next=proxavisitar(v);
13 while(next !=-1)
14 if(next==j) return true;
15 v[next ]=2;
16 aux=0;
17 while(aux <v.length)
18 if(adjacenteQ(next ,aux) && v[aux ]==0) v[aux ]=1;
19 aux ++;
20
21 next=proxavisitar(v);
22
23 return false;
24
Exercıcio 10.3. Indique a complexidade computacional da solucao apresentada.
102
Capıtulo 10. Algoritmos em grafos
Exercıcio 10.4. Implemente o tipo de dados grafo usando listas de adjacencia, incluindo a operacao de acessivelQ.
Obtenha uma implementacao que seja O(|N|).
SECCAO 10.3
Busca em Largura
Em teoria dos grafos, a busca em largura tambem conhecida por busca em amplitude e que deriva
do Ingles Breadth-First Search (BFS) e um algoritmo de procura em grafos utilizado para realizar uma
busca ou travessia num grafo e estrutura de dados do tipo arvore. O objectivo da busca em largura e
pesquisar primeiro os nos que se encontram mais proximos do no inicial ou seja, comecando pelo vertice
raiz, explorar todos os vertices vizinhos so depois passar para cada um desses vertices mais proximos,
exploramos os seus vertices vizinhos ainda nao explorados e assim sucessivamente ate que o elemento
procurado seja encontrado.
Para tal utiliza-se uma fila para guardar o nos que ja foram pesquisados. No exemplo seguinte mostra-
se intuitivamente a ordem pela qual a busca em largura procura os elementos numa arvore (um tipo
particular de grafos!).
Figura 10.1: Exemplo de ordem de procura do algoritmo de busca em largura
103
10.3 Busca em Largura
O algoritmo de busca em largura necessita da seguinte classe FilaP:
1 Class FilaP
2 boolean vaziaQ ();
3 int primeiro ();
4 void retira () /* retira sempre o primeiro elemento */
5 FilaP copia();
6
cuja implementacao pode ser realizada por:
1 public class Filadyn implements Filap
2 protected Node first ,last;
3
4 public filadyn ()
5 first=null;
6 last=null;
7
8
9 public void insere(int i) /* insere no fim da lista*/
10 Node aux= new Node();
11 aux.val=i;
12 aux.next=null;
13 if(last!=null)
14 last.next=aux;
15 last=aux;
16 else
17 last=aux;
18 first=aux;
19
20
21
22 public boolean vazia()
23 return first==null;
104
Capıtulo 10. Algoritmos em grafos
24
25
26 public void retira () /* retira o primeiro da lista*/
27 Node aux=first;
28 if(aux==null) return;
29 if(aux.next==null)
30 first=null;
31 last=null;
32
33 else
34 aux=aux.next;
35 first=aux;
36
37
38
39 public int ultimo ()
40 if(last!=null) return last.val;
41 return -1;
42
43
44 public int primeiro ()
45 if(first!=null) return first.val;
46 return -1;
47
48
49 public FilaP copia ()
50 FilaP n= new FilaP ();
51 Node aux=first;
52 while(aux!=null)
53 n.insere(aux.val);
54 aux=aux.next;
55
56 return n;
57
58
105
10.3 Busca em Largura
A implementacao da busca em largura e dada pelo seguinte metodo:
1 int [] BSF(int v)
2 int numnos=LA.length;
3 int[] order = new int[numnos ];
4 int[] visitados = new int[numnos ];
5 // 0 nao visitados , 1 colocados na lista w, 2 ja visitados
6 FilaP w=new FilaP; /* lista que guarda os nos a tratar */
7 w.add(v);
8 FilaP ladj; /* lista de nos (inteiros) auxiliar */
9 int i=0,cur_ord =0; nextno;
10 while(i<numnos) visitados[i]=0;i++;
11 order[v]= cur_ord;
12 while (!w.vaziaQ ())
13 nextno=w.primeiro ();
14 visitados[nextno ]=2;
15 ladj=adjacentes(nextno);
16 cur_ord ++;
17 order[nextno ]= cur_ord;
18 while (!ladj.vaziaQ ())
19 if(visitados[ladj.primeiro () ]==0)
20 w.add(ladj.primeiro ());
21 visitados[ladj.primeiro ()]=1;
22
23 ladj.retira ();
24
25 w.retira ();
26
27 return order;
28
Deixa-se ao cuidado do leitor verificar que o algoritmo esta correto determinando os invariantes da
pesquisa em largura?
Exercıcio 10.5. Mostre que complexidade deste algoritmo e O(|E|) em |E| e o numero de arestas.
106
Capıtulo 10. Algoritmos em grafos
Em vez de se fazer uma busca em largura podemos implementar uma pesquisa em profundidade,
nome que deriva do Ingles Deep First Search. A diferenca e que a pesquisa e feita de explorando
primeiro todos os descendentes de um no e so quando nao houver mais folhas explorar outro ramo.
Para implementar a pesquise em profundidade basta:
1. substituir a fila w por uma pilha;
2. substituir a chamada de primeiro da fila para ultmio;
3. alterar o ciclo interior e o cur ord para que se coloque apenas um filho na pilha.
O exemplo que se segue mostra a ordem pela qual os elementos de um grafo sao marcados com a
pesquisa em profundidade.
Figura 10.2: Exemplo de ordem de procura do algoritmo de pesquisa em profundidade
107
10.4 Arvore de Extensao Mınima
SECCAO 10.4
Arvore de Extensao Mınima
Considere um grafo pesado G = (V, E) onde cada aresta tem um peso w : E → R, i.e., uma funcao que
atribui um peso a cada uma das arestas. O problema da arvore de Extensao Mınima e encontrar um
subgrafo acıclico T que contem todos os vertices de G e tal que o seu peso
w(T) = ∑e∈T
w(e)
e mınimo.
Note-se que este subgrafo acıclico forma uma arvore que se chama Arvore de Extensao Mınima (AEM).
Definicao 10.2
Um corte de um grafo G e uma particao S, V \ S de V.
Uma aresta cruza um corte se a sua origem e destino se encontram em partes diferentes da particao.
Uma aresta diz-se leve para um corte S, V \ S se o seu peso e mınimo entre todas as arestas que
cruzam o corte.
O teorema seguinte da a intuicao para o algoritmo de Prim.
Teorema 10.3
Seja S ⊆ T onde T e uma AEM e seja e uma aresta leve para S, V \ S.
Entao existe uma AEM T′ tal que S ∪ e = S′ ⊆ T′.
Demonstracao : Suponha que nao existe tal arvore T′ e considere-se a arvore T′′ tal que T′′ ∩ S = T ∩ S e T′′ ∩
V/S = T ∩V/S e tal que a aresta leve e de S pertence a T′′.
Note que T′′ e uma AEM para S e T∩V/S e uma AEM para V/S pois caso contrario T nao seria uma AEM. Seja e′ a
aresta em T′ que cria em T′′ um ciclo e seja w a funcao de peso total de S. Entao w(T′′) = w(T)−w(e′)+w(e).Logo
w(T′′) ≤ w(T) pois w(e′) ≥ w(e) e por e ser leve. Assim T′′ e a arvore de extensao mınima.
O algoritmo de Prim e descrito em Java pelo seguinte codigo:
108
Capıtulo 10. Algoritmos em grafos
1 int [] MST_Prism ()
2 int numnos=LA.length;
3 int [] pai=new int[numnos ]; /* lista de pais */
4 Lista S=new Lista ();
5 int i=0;
6 int[] e;
7 while(i<numnos)
8 pai[i]=-1;
9 i++;
10
11 S.add(0);
12 while (S.comprimento != numnos)
13 e=leve(S); /* retorna uma aresta leve de S,V\S */
14 pai[e[1]]=e[0];
15 S=add(S,e[1]);
16
17 return pai;
18
Figura 10.3: Exemplo de algoritmo de Prim
109
10.4 Arvore de Extensao Mınima
10.4.1 Grafos planares
Um grafo diz-se planar se pode ser representado no plano de tal forma que suas arestas nao se cruzem.
Um grafo G′ diz-se uma subdivisao de um grafo G se existe uma sequencia G0 . . . Gn de grafos onde
G = G0 e G′ = Gn tal que Gi+1 e obtido de Gi substituindo uma aresta entre dois nos i, j por duas arestas
(i, k)(k, j) onde k e um no nao existente em Gi.
Teorema 10.4 (de Kuratowski)
Um grafo e planar se e so se nao tem como subgrafo uma subdivisao de K5 e K3,3.
Exercıcio 10.6. Implemente o teste de Kuratowksi em C (Nota: verificar se um grafo e isomorfo a um dado
subgrafo e NP-Completo).
Exercıcio 10.7. Investigue metodos mais eficientes para testar a planaridade um grafo, nomeadamente implemente
um metodo linear no numero de vertices.
110
AULA
11Leitura e escrita em ficheiro usando Java
Temas abordados
• Serializacao;
• Escrita e leitura de ficheiros;
• Excepcoes;
• Codigo para leitura e escrita em ficheiros usando Java;
111
11.1 Serializacao
SECCAO 11.1
Serializacao
Em java existem duas formas de tratar ficheiros, como um fluxo baseado em bytes armazenando os seus
valores em formato binario. Surge assim o conceito de serializacao. Serializacao refere-se a possibilidade
de se poder armazenar em disco objetos construıdos em Java transformado-os em bytes. Tambem se
podera transmitido por um stream. Stream e uma forma de transmissao de dados feita atraves de uma
origem e de um destino. Os fluxos mais utilizados para stream sao:
FileOutputStream que permite a gravacao em disco;
FileInputStream que permite a leitura de um ficheiro em disco.
SECCAO 11.2
Escrita e leitura de ficheiros em java
A escrita e leitura de ficheiros em Java pressupoe que os ficheiros tem um formato muito caracterıstico –
Comma-separated values (CSV), que em portugues se traduz para Valores Separados por Vırgula. E um
formato de arquivo que armazena dados tabelados,e que por serem bastante simples, os ficheiro .csv
sao comuns em todas as plataformas de computador.
O formato .csv e uma implementacao particular de ficheiros de texto separados por um delimitador,
usualmente a vırgula e a quebra de linha para separar os valores. O formato tambem usa as aspas em
campos no qual sao usados os caracteres reservados (vırgula e quebra de linha).
1 import java.io.*;
2 import java.util.Arrays;
3
4 public class CSVReader
5
6 public static void main(String [] arg) throws Exception
7 // throws indica ao Java para dar erro caso o ficheiro nao exista
112
Capıtulo 11. Leitura e escrita em ficheiro usando Java
8
9 BufferedReader CSVFile = new BufferedReader(new FileReader("
Exemplo.csv"));
10
11 String dataRow = CSVFile.readLine (); // Le a primeira linha.
12
13 while (dataRow != null)
14 // Qd chega ao fim o dataRow fica a null
15 String [] dataArray = dataRow.split(",");
16 // split() metodo que separa os dados da string num array
17 int[] intArray=new int[dataArray.length ];
18 //vari’avel intArray vai guardar os dados como inteiros;
19 int i=0;
20 while(i<dataArray.length)
21 intArray[i]= Integer.parseInt(dataArray[i]);
22 // parseInt transforma uma string num inteiro
23 i++;
24
25 System.out.println(Arrays.toString(intArray)); // Imprime um
Array
26 dataRow = CSVFile.readLine (); // Le a proxima linha
27
28 CSVFile.close();
29 // Fechar o ficheiro
30 System.out.println ();
31
32
O comando import java.io.* permite importar todas as classes implementadas para java.io. Este
pacote contem ja as instrucoes de como escrever e ler de ficheiros com os fluxos FileOutputStream e
FileInputStream.
O comando BufferedReader CSVFile carrega para memoria o ficheiro csv.
O comando dataRow.split(",") serve para colocar num vector os dados e de modo a se poder mani-
113
11.2 Escrita e leitura de ficheiros em java
pular os valores do ficheiro, nao como strings, mas como inteiros, pode-se usar Integer.parseInt.
Se for necessario imprimir um vector de uma vez so e nao elemento a elemento e necessario importar a
classe java.util.Arrays que tem um metodo proprio para imprimir de uma vez so.
Um aspeto importante da manipulacao de ficheiro e a necessidade de estes serem fechados no fim da
sua utilizacao atraves do comando close.
No que se segue implementam-se metodos para ler e escrever em ficheiros usando objetos do tipo Fila.
1 import java.io.*;
2 public class FilaDynIO extends Filadyn implements java.io.Serializable
3
4 private static final long serialVersionUID = 1L;
5 private ObjectOutputStream out;
6 private ObjectInputStream in;
7
8 public void Save(String s) throws IOException
9 FileOutputStream fos = new FileOutputStream(s);
10 out = new ObjectOutputStream(fos);
11 out.write(this.length ());
12 Node aux=first;
13 while(aux!=null)
14 out.write(aux.val);
15 aux=aux.next;
16
17 out.close();
18 fos.close();
19
20
21
22 public void Save(ObjectOutputStream out) throws IOException
23 out.write(this.length ());
24 Node aux=first;
25 while(aux!=null)
26 out.write(aux.val);
27 aux=aux.next;
114
Capıtulo 11. Leitura e escrita em ficheiro usando Java
28
29 out.close();
30
31
32
33 public void Load(String s) throws IOException
34 FileInputStream fis = new FileInputStream(s);
35 in = new ObjectInputStream(fis);
36 first=null;
37 last=null;
38 int l=in.read();
39 int i=0;
40 while(i<l)
41 insere(in.read());
42 i++;
43
44 in.close();
45 fis.close();
46
47
48 public void Load(ObjectInputStream in) throws IOException
49 first=null;
50 last=null;
51 int l=in.read();
52 int i=0;
53 while(i<l)
54 insere(in.read());
55 i++;
56
57
58
59 public static void main(String [] arg) throws Exception
60 FilaDynIO f= new FilaDynIO ();
61 f.insere (1);
62 f.insere (2);
63 f.Save("fila.dyn");
115
11.2 Escrita e leitura de ficheiros em java
64 f.Save("fila.dyn");
65 FilaDynIO g=new FilaDynIO ();
66 g.Load("fila.dyn");
67 System.out.println(g.length ());
68
69
O leitor certamente deu conta que existem duas implementacoes de save e load. E entao pertinente
perceber a diferenca entre os dois metodos. O primeiro implementa a ideia de lidar com um objeto e
que no fim e fechado, o que significa que se se pretende, por exemplo, escrever para um ficheiro mais do
que um dado, este metodo vai escrever por cima do que ja la estava enquanto que o outro como recebe
um objeto de tipo diferente e nao fecha o ficheiro em causa permite acrescentar mais dados.
116
Parte II
Projeto
117
Greedy Hill Climber para
Aprendizagem de BNC’s
SECCAO 1.1
Objetivo
O objetivo do projeto e desenvolver um classificador baseado em redes de Bayes. O classificador e
aprendido a partir de dados publicos que sao fornecidos na pagina da disciplina. Estes dados provem
do UCI machine learning repository.1
A qualidade do classificador sera avaliada por intermedio de um metodo chamado stratified cross valida-
tion.
Chama-se a atencao que, apesar de o exemplo a aplicar neste projeto se concentrarem em aplicacoes
biomedicas nomeadamente diagnostico de doencas e funcionalidade de farmacos, o domınio de aplicacao
do mesmo e muito mais extenso, incluindo por exemplo: OCR, previsao da bolsa de valores e de
resultados de eventos desportivos, etc.
1http://archive.ics.uci.edu/ml/
119
1.2 Conceitos basicos
SECCAO 1.2
Conceitos basicos
1.2.1 Classificador
Um classificador sobre um domınio D e simplesmente um mapa f : D → C onde C e chamado o conjunto
de classes. Por exemplo, para o caso da base de dados Cancer, o conjunto de classes e C = benign,
malignant e um elemento em D corresponde a um tuplo de dez medicoes sobre o tumor. Nos casos
de interesse, o domınio e sempre estruturado da seguinte forma: D =n
∏i=1
Di onde n e o numero de
medicoes e Di e o domınio da i-esima medicao. Assim, um elemento d ∈ D e da forma d = (d1, . . . , dn).
1.2.2 Dados
O classificador e construıdo (ou aprendido) a partir de um conjunto de dados T. Os dados sao uma
amostra de elementos do domınio e respetiva classe ou seja T = T1, . . . , Tm e Tj = (d1j, . . . , dnj, cj)
onde m e a dimensao dos dados, di,j ∈ Di, cj ∈ C para todo o 1 ≤ i ≤ n e 1 ≤ j ≤ m. Como os dados sao
discretizados, isto e Di ⊆N, podemos ver os dados como uma matriz m× (n + 1) de entradas naturais.
1.2.3 Classificar vs estimar
Uma maneira simples de classificar consiste em inferir a distribuicao que gera os dados (ha muitas
outras maneiras). Sejam X1 . . . Xn e C variaveis aleatorias para as quais os dados T sao uma amostra
multinomial do vector aleatorio ~V = (X1, . . . , Xn, C). O objectivo de classificar pode-se reduzir a inferir
a distribuicao deste vector da seguinte forma
f (d1, . . . , dn) = c
tal que Pr(~V = (d1, . . . , dn, c)) > Pr(~V = (d1, . . . , dn, c′)) para c′ 6= c.
Por outras palavras, sabendo a distribuicao do vector ~V, classificar um elemento do domınio reduz-
se a escolher o elemento da classe que maximiza a probabilidade de observar o elemento do domınio
120
Capıtulo 11. Leitura e escrita em ficheiro usando Java
com este elemento da classe (ou seja f e o estimador de maxima verosimilhanca para a classe dado o
elemento do domınio).
Note que a dimensao do domınio D cresce exponencialmente com o numero de variaveis, e portanto
inferir a distribuicao (multinomial) do vector V utilizando a lei dos grandes numeros2 requer dados
de dimensao exponencial no numero de variaveis para obter distribuicoes proximas das distribuicoes
reais. Nestas condicoes, quando se utilizam dados pequenos, a distribuicao obtida fica muito enviesada
aos dados, fenomeno a que se da o nome de overfitting.
1.2.4 Redes de Bayes
Para ultrapassar a limitacao de nao se possuir dados suficientemente grandes, supoe-se que existem
dependencias diretas entre as variaveis e que estas dependencias estao descritas num grafo acıclico
G = (X , E) onde X = X1, . . . Xn, C tal que (C, Xi) ∈ E para 1 ≤ i ≤ n. O facto de todas as variaveis
Xi dependerem de C prende-se com o facto de que, em princıpio, Xi nao e independente de C, pois
caso contrario Xi nao serve para classificar (ou estimar) C. Assim podemos decompor a distribuicao de
probabilidade do vector ~V da seguinte forma
Pr(~V = (d1, . . . , dn, c)) = Pr(C = c)n
∏i=1
Pr(Xi = di|Πi = (di,1 . . . di,ki , c)) (1.2.1)
onde Πi = (Xi,1, . . . , Xi,ki , C) e um vector constituıdo pelos pais de Xi no grafo G.
Assim para obter a distribuicao de ~V basta conhecer as distribuicoes C e Xi|Πi . Note que como todo
os dados estao discretizados, DC (o domınio da variavel classe C) e Di (o domınio da variavel Xi) sao
finitos, as variaveis C e Xi|Πi sao variaveis multinomiais. Neste caso ja se torna possıvel estimar as
distribuicoes C e Xi|Πi , utilizando a lei dos grande numeros, mesmo com dados relativamente pequenos.
Com generalidade, uma rede de Bayes e um tuplo (G, Θ) onde Θ = Θi|wii∈N,wi∈DΠi
e Θi|wie uma
distribuicao multinomial para a variavel Xi e DΠi e o domınio dos pais de Xi em G. Fixado um grafo G
as distribuicoes multinomiais em Θ que maximizam a verosimilhanca dos dados T sao dadas por
Θi|wi(di) =
|Tdi ,wi ||Twi |
2Prob(~V = (d1, . . . , dn, c)) = limm→∞
|i ≤ m : Ti = (d1, . . . , dn, c)|m
e T e uma amostra arbitrariamente grande.
121
1.2 Conceitos basicos
onde Tdi ,wi e o conjunto de amostras de T onde a variavel Xi toma o valor di e os seus pais tomam o valor
wi e, de forma semelhante Twi e o conjunto de amostras de T onde os pais de Xi tomam o valor wi. Caso
Twi seja vazio, Θi|widevera ser uniforme. Esta distribuicao e chamada a distribuicao das frequencias
observadas (DFO).
No entanto, observe que a DFO impoe que se |Tdi ,wi | = 0 entao Θi|wi(di) = 0, ou seja o facto de nao
observarmos um certo evento significa que este vai ter probabilidade 0. Isto nao e considerado certo,
pois os dados podem nao ter dimensao suficiente para indicar que certo evento e impossıvel. Assim
sendo considera-se que todos os eventos ocorreram pelo menos S vezes (a este S chama-se pseudo-
contagem) e estima-se que
Θi|wi(di) =
|Tdi ,wi |+ S|Twi |+ S× |Di|
.
Assim eventos raros nunca tem probabilidade 0. Tipicamente considera-se S = 0.5.
1.2.5 Aprendizagem de Redes de Bayes
Pelo o que foi apresentado anteriormente, para aprender redes Bayes dado T basta aprender o grafo
orientado G ja que Θ e obtido das DFO’s. Encontrar o grafo que maximiza a verosimilhanca de T e
um problema NP-completo e para o qual nao se espera haver solucao eficiente. Mais, ao maximizar
a verosimilhanca obtem-se grafos completos e nao grafos esparsos. Mas mais uma vez, para grafos
acıclicos completos as DFO’s associadas a dados pequenos fazem overfitting. A solucao e restringir a
aprendizagem a grafos com estruturas mais simples, e no caso deste projeto so serao aprendidos grafos
com grau de entrada ate um certo limite k com k ≤ 4.
Como derivado no quadro da aula teorica, o grafo G que maximiza a verosimilhanca de T e o grafo que
maximiza
LL(G|T) = Nn
∑i=1
IT(Xi; Πi|C).
Note que IT(Xi; Πi|C) e a informacao mutua condicional de Xi e do vector aleatorio Πi dado C medida
com a distribuicao de probabilidade obtida pela DFO (sem pseudo-contagens). A expressao para a
informacao mutua condicional e dado por
IT(X; Y|C) = ∑x,y,c
PrT(x, y, c) log(
PrT(x, y, c)PrT(c)PrT(y, c)PrT(x, c)
)
122
Capıtulo 11. Leitura e escrita em ficheiro usando Java
onde PrT(x, y, c) =Nx,y,c
N, PrT(x, c) =
Nx,c
N, PrT(y, c) =
Ny,c
Ne PrT(c) =
Nc
N; e Nx,y,c e o numero de vezes
que nos dados X toma o valor x, Y toma o valor y e C toma o valor c (e semelhante para os outros).
Como encontrar o grafo que maximiza o LL e NP-Hard, a abordagem consiste em gerar um conjunto
de grafos aleatorios (com grau de entrada ate k) e depois adicionar/retirar arestas de forma a aumentar
ao maximo o LL em cada passo. O procedimento para quando nao houver mais ganho a fazer. Como o
numero possıvel de arestas a acrescentar/retirar e polinomial, o algoritmo corre em tempo polinomial
em cada passo. A este algoritmo chama-se Greedy Hill Climber (GHC). Um facto reconhecido e que
o GHC apenas encontra maximos locais, nao se garantindo serem maximos globais. Por atenuar este
efeito, torna-se necessario iniciar o processo com um grafo aleatorio. Um dos grafos que deve ser sempre
escolhido como ponto de partida e o grafo sem aresta nenhuma entre os nos Xi, e que tem apenas as
arestas de C para Xi, (dito Naive Bayes Classifier).
1.2.6 Minimum Description Length (MDL)
Uma propriedade indesejavel da verosimilhanca e que
IT(Xi; Πi|C) ≤ IT(Xi; Πi|C)
se Πi ⊆ Πi. Uma consequencia e que para a verosimilhanca, o GHC ira sempre acrescentar arestas (e
nunca retirar). Pior e que a rede Bayes obtida fica demasiado enviesada (overfitting) aos dados, por ter
excesso de pais por no. A solucao e considerar a abordagem da navalha de Occam (Occam’s razor): a
explicacao mais simples e a melhor. Para tal deriva-se uma penalizacao (dita minimum description length)
baseada em teoria da informacao que penaliza estruturas muito complexas. O MDL de uma rede de
Bayes e dada por
MDL(G|T) =log2 N
2|Θ| − N
n
∑i=1
IT(Xi; Πi|C) (1.2.2)
onde |Θ| = |DC| +n
∑i=1
(ki − 1) × qi e o numero de parametros de uma rede de Bayes e ki = |Di| e
|qi| = |DΠi | = |DC| × ∏Xj∈Πi
|Dj|.
Note que minimizar a Equacao 1.2.3 e equivalente a maximizar
MDLs(G|T) = Nn
∑i=1
IT(Xi; Πi|C)−log2 N
2|Θ|. (1.2.3)
123
1.3 Tipos de dados para a 1a entrega
SECCAO 1.3
Tipos de dados para a 1a entrega
Os tipos de dados a serem utilizados neste projeto sao os seguintes:
1.3.1 Amostra
• add: recebe um vector e acrescenta o vector a amostra;
• length: retorna o comprimento da amostra;
• element: recebe uma posicao e retorna o vector da amostra;
• count: recebe um vector de variaveis e um vector de valores e retorna o numero de ocorrencias
desses valores para essas variaveis na amostra;
• join: recebe uma amostra e concatena-a a amostra;
1.3.2 Grafos orientados
• grafoo: metodo construtor recebe um natural n e retorna o grafo com n nos e sem arestas.
• add edge: recebe dois nos e adiciona ao grafo uma aresta de um no para outro.
• add edge: recebe dois nos e retira ao grafo uma aresta de um no para outro.
• parents: recebe um no e retorna a lista de nos que sao pais do no.
• MDLdelta: recebe uma amostra e dois nos e retorna a variacao de MDL causada por retirar ou
colocar aresta entre os nos.
• MDL: recebe uma amostra e retorna o MDL score da amostra.
1.3.3 Redes Bayesianas
• BN: Metodo construtor que recebe um grafo, um conjunto de dados e um double S e retorna a rede
de Bayes com as distribuicoes DFO amortizadas com pseudo-contagens S.
124
Capıtulo 11. Leitura e escrita em ficheiro usando Java
• prob: Recebe uma rede de Bayes e um vector e retorna a probabilidade desse vector.
125
2a Entrega
Na segunda entrega deverao ser implementadas duas aplicacoes principais, ambas com interface grafica:
• Uma aplicacao que le a amostra, aprende uma rede de Bayes e grava-a no disco;
• A aprendizagem e feita por um algoritmo greedy que comeca com uma rede de Bayes aleatoria e
vai adicionando ou removendo arestas ate maximizar o MDL. O numero maximo de pais deve ser
um parametro da aplicacao grafica, bem como o numero de grafos aleatorios com que se inicializa
o processo de aprendizagem. O grafo totalmente desconexo deve ser sempre considerado como
um ponto inicial.
• Uma aplicacao que le a rede de Bayes do disco, permite escrever os parametros do paciente e
classifica-o.
• Devera ser submetido um relatorio com a explicacao das opcoes tomadas e alteracoes realizadas
na 1a parte do projecto.
• As amostras a considerar devem ter S=0.5 e estao na pagina da unidade curricular: Breast Cancer;
Diabetes; Hepatitis; Parkinsons; Thyroid.
127
Bibliografia
[CSRL01] T. Cormen, C. Stein, R. Rivest, and C. Leiserson. Introduction to Algorithms. McGraw-Hill
Higher Education, 2nd edition, 2001.
[CSS+04] J. Carmo, A. Sernadas, C. Sernadas, F. M. Dionısio, and C. Caleiro. Introducao a Programacao
em Mathematica – Segunda Edicao (Introduction to Programming in Mathematica – Second Edition).
IST Press, 2004.
[Eck02] B. Eckel. Thinking in Java. Prentice Hall Professional Technical Reference, 3rd edition, 2002.
[MS13] P. Mateus and A. Souto. Aulas de Algoritmos e Modelacao Matematica. IST - em preparacao,
2013.
129