DAA 2015/2016 (CC2001) DCC/FCUP Aula Prática 2 Previsão, Ciclos e Somatórios Ordenação e Dividir para Conquistar Para efeitos da nota atribuida à resolução de exercı́cios ao longo do semestre, os exercı́cios a submeter desta aula são: • [03] - Ordenando Números (peso 10%) • [04] - Tabela Classificativa (peso 40%) • [05] - Inversões (peso 50%) Previsão de Tempo de Execução 1. Imagine que tem um programa P implementado, que recebe como input n números. Ao experimentar executá-lo com testes aleatorizados, obteve os seguintes tempos de execução: • Com n = 300 demorou 1.5 segundos • Com n = 600 demorou 12.0 segundos • Com n = 1200 demorou 1m36s (a) Qual será a complexidade assintótica mais provável do programa P ? Justifique. (b) Estime quanto tempo demoraria o programa para um caso com 5, 000 números. Ciclos e Somatórios 2. Quais os valores exactos da variável contador no final dos seguintes pedaços de código quadráticos? Escreva a sua resposta como uma função de n. Se tiver dificuldades pode experimentar implementar os programas na sua linguagem de programação favorita e verificar empiricamente qual o valor da variável para vários valores de n. (a) contador ← 0 Para i ← 1 até n fazer Para j ← i até n fazer contador ← contador + 2 (b) contador ← 0 Para i ← 1 até n fazer Para j ← i + 1 até n − 1 fazer contador ← contador + 1 (c) contador ← 0 Para i ← 1 até n fazer Para j ← 1 até 2 × i fazer contador ← contador + 3 1 Usando a biblioteca da sua linguagem para ordenar 3. A ordenação é uma operação ”básica” essencial em muitos algoritimos. É por isso comum que uma qualquer linguagem de programação tenha disponı́vel na sua bilbioteca padrão uma função de ordenação, O objectivo deste exercı́cio é levá-lo a conhecer como ordenar usando o sort disponı́vel na sua linguagem preferida. Iremos usar as seguintes funções (escolha a da linguagem que pretende usar): • C: qsort da stdlib (ver entrada nas manpages) • C++: sort da STL (ver entrada no cplusplus.com) • Java: Arrays.sort (ver entrada da referência da API do Java) (a) Na página da cadeira é disponibilizado um pequeno programa para ordenar 10 números inteiros (sort.c, sort.cpp ou Sort.java). Descarregue o código da sua linguagem de eleição e experimente compilar e executar na sua máquina, garantindo que percebe o programa. (b) Faça as (poucas) modificações necessárias no programa anterior para submeter uma solução para o problema [03] - Ordenando Números usando a biblioteca da sua linguagem. Se obtiver um Presentation Error não se esqueça que não podem existir espaços a mais ou a menos no output. Ordenação com um critério personalizado 4. Por vezes queremos ordenar por um critério diferente do padrão (ex: por ordem decrescente; ordenar primeiro por um campo e depois por outro). Nesses casos queremos poder usar o sort da biblioteca, mas passar-lhe um comparador ”customizado” que permita ordenar segundo o critério que desejamos. (a) Na página da cadeira é disponibilizado um pequeno programa para ordenar nomes por ordem alfabética do apelido e em caso de empate por ordem alfabética do primeiro nome (customsort.c, customsort.cpp ou CustomSort.java). Tem também disponı́vel um ficheiro com 160 nomes para poder testar o programa: (names.txt) Descarregue o código da sua linguagem de eleição e experimente compilar e executar na sua máquina, garantindo que percebe o programa. Para o fazer use a seguinte linha de comando, que compila e, caso consiga compilar com sucesso, executa o programa com o input do ficheiro names.txt, escrevendo para o ficheiro output.txt (C) gcc -Wall -o customsort customsort.c && ./customsort < names.txt > output.txt (C++) g++ -Wall -o customsort customsort.cpp && ./customsort < names.txt > output.txt (Java) javac CustomSort.java && java CustomSort < names.txt > output.txt Para poder verificar o output pode por exemplo usar o comando cat: cat output.txt (b) Submeta o problema [04] - Tabela Classificativa. 2 Contando Inversões 3. Leia o problema [05] - Inversões, disponı́vel na página de DAA. (a) Vamos tentar fazer uma primeira solução com pesquisa exaustiva (aka ”força bruta”), testando todos os pares de números. Supondo que já temos a leitura feita para o array v[]: contador ← 0 Para i ← 0 até n − 1 fazer Para j ← i + 1 até n − 1 fazer Se v[i] > v[j] então contador ← contador + 1 escrever(contador) Perceba o que faz o código acima descrito e implemente este programa em C/C++ ou Java. Usando a notação dada, qual é a complexidade assintótica do algoritmo usado? Submeta o programa no Mooshak e verifique que o último testes não passa por causa do tempo limite. (b) Vamos tentar melhorar esta solução para Θ(n log n). Para isso vamos adaptar o MergeSort e criar uma solução usando o paradigma ”Dividir para Conquistar” que ao mesmo tempo que vai ordenando vai contando as inversões. Na página da cadeira é disponibilizada uma implementação exmeplo do MergeSort:(mergesort.c, mergesort.cpp ou MergeSort.java). Descarregue o código da sua linguagem de eleição e experimente compilar e executar na sua máquina, garantindo que percebe o programa. Depois de o fazer poder até submeter novamente o problema [03] - Ordenando Números com este código. (c) Como adaptar o código anterior para contar inversões? A função mergesort(int v[], int start, int end) deve passar a devolver um inteiro: o número de inversões. Para calcular com divisão e conquista, o número de inversões total deverão ser a soma das inversões somente da parte esquerda, das inversões somente da parte direita... e das inversões entre pares de partições diferentes! O esqueleto da sua função deverá ser algo como: contador = 0; contador += mergesort(v, start, middle); contador += mergesort(v, middle+1, end); contador += merge(v, start, middle, end); return contador; Os dois primeiros incrementos estão já feitos (assumindo que a função funciona). A única coisa que lhe falta fazer é calcular o número de inversões entre as duas partições (ordenadas). Consegue imaginar como fazer? Pense um pouco por si antes de pedir ajuda, ou usar o Google :) Submeta uma solução Θ(n log n) para o problema [05] - Inversões 3