Programação Imperativa. Lição n.º 12 Arrays ordenados

Documentos relacionados
Análise e Complexidade de Algoritmos

Programação imperativa. 3. Mais funções

Laboratório de Programação. Lição n.º 3 Somas acumuladas

Algoritmos e Estruturas de Dados 2005/2006. Algoritmo: conjunto claramente especificado de instruções a seguir para resolver um problema

Algoritmos e Estruturas de Dados. Décima sexta aula: Quicksort

ORDENAÇÃO POR INTERCALAÇÃO

AED2 - Aula 11 Problema da separação e quicksort

Programação Imperativa. Lição n.º 3 Operações aritméticas

Medida do Tempo de Execução de um Programa. David Menotti Algoritmos e Estruturas de Dados II DInf UFPR

Pesquisa: operação elementar

Análise de Problemas Recursivos. Algoritmos e Estruturas de Dados Flavio Figueiredo (

Algoritmos de Busca em Vetores

Centro Federal de Educação Tecnológica de Minas Gerais Programa de Pós-Graduação em Modelagem Matemática e Computacional

Medida do Tempo de Execução de um Programa. Bruno Hott Algoritmos e Estruturas de Dados I DECSI UFOP

Ordenação por Intercalação Métodos de Ordenação Parte 4

Recursividade. Objetivos do módulo. O que é recursividade

CT-234. Estruturas de Dados, Análise de Algoritmos e Complexidade Estrutural. Carlos Alberto Alonso Sanches

MC102 - Algoritmos e programação de computadores. Aula 16: Busca e Ordenação em vetores

Ordenação: MergeSort. Prof. Túlio Toffolo BCC202 Aula 14 Algoritmos e Estruturas de Dados I

Introdução à Análise de Algoritmos

Programação imperativa. 18. Busca dicotómica

BCC202 - Estrutura de Dados I

ALGORITMOS E ESTRUTURAS DE DADOS 2011/2012 ANÁLISE DE ALGORITMOS. Armanda Rodrigues 3 de Outubro 2011

Métodos de Ordenação Parte 3

PROGRAMAÇÃO E ALGORITMOS (LEI) Universidade da Beira Interior, Departamento de Informática Hugo Pedro Proença, 2016/2017

Medida do Tempo de Execução de um Programa. David Menotti Algoritmos e Estruturas de Dados II DInf UFPR

Programação Imperativa. Lição n.º 17 Cadeias de carateres

ÁRVORES BINÁRIAS DE BUSCA. Vanessa Braganholo Estruturas de Dados e Seus Algoritmos

Algoritmos e Estruturas de Dados LEE 2013/2014. popular devido à facilidade de implementação e eficiência

Análise de Algoritmos

CES-11. Noções de complexidade de algoritmos. Complexidade de algoritmos. Avaliação do tempo de execução. Razão de crescimento desse tempo.

Aula 18 Algoritmos básicos de busca e classificação

Análise de Algoritmos Estrutura de Dados II

Estrutura de Dados. Algoritmos de Ordenação. Prof. Othon M. N. Batista Mestre em Informática

INF1007: Programação 2 6 Ordenação de Vetores. 01/10/2015 (c) Dept. Informática - PUC-Rio 1

QuickSort. Algoritmos e Estruturas de Dados Verão Cátia Vaz 1

Algoritmos e Estruturas de Dados I Linguagem C

HeapSort Filas de Prioridade Heap. David Menotti Algoritmos e Estruturas de Dados II DInf UFPR

CURSO DE ESTRUTURA DE DADOS MÓDULO: ALGORITMOS DE ORDENAÇÃO E PESQUISA PROFESSORA: DANIELA ELOISE FLÔR COLABORADORA: MARIA CAROLINA SILA VANUCHI

Aula 13: Ordenação - Heapsort. Bruno Hott Algoritmos e Estruturas de Dados I DECSI UFOP

Aula 12- Variáveis e valores reais

Modelagem Computacional. Parte 2 2

Programação: Vetores

Aula T19 BCC202 Pesquisa (Parte 1) Pesquisa Binária. Túlio Toffolo

Projeto e Análise de Algoritmos

Área que visa determinar a complexidade (custo) de um algoritmo, com isso é possível:

Algoritmos e Complexidade

UNIVERSIDADE DA BEIRA INTERIOR

ESTRUTURAS DE DADOS E ALGORITMOS APRESENTAÇÃO DO CURSO E INTRODUÇÃO

CIC 110 Análise e Projeto de Algoritmos I

Análise e Complexidade de Algoritmos

Programação II. Árvores Binárias (Binary Trees) Bruno Feijó Dept. de Informática, PUC-Rio

ESTRUTURAS DE DADOS E ALGORITMOS ALGORITMOS DE ORDENAÇÃO POR COMPARAÇÃO - II

Análise de complexidade

DCC008 - Cálculo Numérico

Introdução à Programação C

Centro de Informá-ca Universidade Federal de Pernambuco. Vinicius Cardoso Garcia 2011 Vinicius Cardoso Garcia

Programação de Computadores II. Cap. 17 Busca

Algoritmos de Ordenação: QuickSort

Algoritmos e Estrutura de Dados

Ordenação: QuickSort. Prof. Túlio Toffolo BCC202 Aula 15 Algoritmos e Estruturas de Dados I

Programação Estruturada

SME Cálculo Numérico. Lista de Exercícios: Gabarito

Recursividade. Prof. Jesus José de Oliveira Neto

Existem infinitos números de Carmichael, mas não provaremos isso neste curso.

ALGORITMOS DE ORDENAÇÃO RECURSIVOS

CIC 110 Análise e Projeto de Algoritmos I

1. Se v é um vetor, qual a diferença conceitual entre as expressões v[70] e v+70? [2 ponto]

Prova 1 PMR2300 / PMR3201 1o. semestre 2015 Prof. Thiago Martins

Algoritmo. Exemplo. Definição. Programação de Computadores Comparando Algoritmos. Alan de Freitas

Árvores. Fabio Gagliardi Cozman. PMR2300 Escola Politécnica da Universidade de São Paulo

Prova 1 PMR3201 Computação para Automação 1o. semestre 2016 Prof. Thiago de Castro Martins

Recursão David Déharbe

5. Análise de Complexidade de Algoritmos. João Pascoal Faria (versão original) Ana Paula Rocha (versão 2003/2004) Luís Paulo Reis (versão 2005/2006)

Quick Sort. Considerações Sobre Algoritmos de Ordenação. Estagiário PAE: Jesimar da S. Arantes Professor: ClaudioQuick F. M.

Divisão e Conquista. Norton T. Roman. Apostila baseada nos trabalhos de Cid de Souza, Cândida da Silva e Delano M. Beder

INF1005: Programação 1. Vetores. 02/05/10 (c) Paula Rodrigues 1

ESTRUTURA DE DADOS (TCC )

Métodos de Ordenação Parte 4

Capítulo 14. Ordenação e pesquisa. Bubblesort. Alguns algoritmos de ordenação e pesquisa Medição do tempo de execução de um programa

d) Defina uma função que copia (replica) um vector de inteiros, tendo o novo vector um tamanho dado como argumento.

Árvores. Thiago Martins, Fabio Gagliardi Cozman. PMR2300 / PMR3201 Escola Politécnica da Universidade de São Paulo

Programação I Aula 7 Resolução numérica de equações

O problema do 3*n Otimização

Análise de Algoritmos

Lista 1 - PMR2300. Fabio G. Cozman 3 de abril de 2013

INF1007: Programação 2 7 Busca em Vetores. 01/04/2014 (c) Dept. Informática - PUC-Rio 1

Pesquisa sequencial e pesquisa binária

05 Análise de Algoritmos (parte 4) SCC201/501 - Introdução à Ciência de Computação II

ESTRUTURA DE DADOS E ALGORITMOS. Árvores Binárias de Busca. Cristina Boeres

AULA 17. Melhores momentos. Segmento de soma máxima. Segmento de soma máxima. Conclusões. Algumas técnicas

3. Vectores: Algoritmos de Pesquisa. João Pascoal Faria (versão original) Ana Paula Rocha (versão 2004/2005) Luís Paulo Reis (versão 2005/2006)

Introdução à Programação Aula 7 Resolução numérica de equações

7. Introdução à Complexidade de Algoritmos

15/03/2018. Professor Ariel da Silva Dias Algoritmo e Contagem de Instruções. Prof. Ariel da Silva Dias -

Árvores & Árvores Binárias

Vectores: Algoritmos de Ordenação. Algoritmos e Estruturas de Dados 2009/2010

COMPLEXIDADE DE ALGORITMOS

Algoritmos Avançados Análise de Complexidade

Transcrição:

Programação Imperativa Lição n.º 12 Arrays ordenados

Arrays ordenados Renque. Busca dicotómica. Método da bisseção. 11/3/16 Programação Imperativa 2

Problema do renque O renque de um valor num array é o número de elementos do array cujo valor é menor que esse valor: int ints_rank_general(const int *a, int n, int x); void unit_test_rank_general(void) int a[10] = 8,3,9,8,4, 4,2,7,5,7; assert(ints_rank_general(a, 10, 5) == 4); assert(ints_rank_general(a, 10, 12) == 10); assert(ints_rank_general(a, 10, 1) == 0); assert(ints_rank_general(a, 10, 2) == 0); assert(ints_rank_general(a, 10, 3) == 1); assert(ints_rank_general(a, 10, 7) == 5); assert(ints_rank_general(a, 5, 7) == 2); assert(ints_rank_general(a, 5, 3) == 0); assert(ints_rank_general(a, 5, 9) == 4); assert(ints_rank_general(a, 1, 5) == 0); assert(ints_rank_general(a, 1, 9) == 1); assert(ints_rank_general(a, 0, 5) == 0); Chamamos renque geral porque se pode aplicar a um array qualquer. Para arrays ordenados, usaremos funções especializadas. 11/3/16 Programação Imperativa 3

Renque geral Programa-se nas calmas: int ints_rank_general(const int *a, int n, int x) int result = 0; for (int i = 0; i < n; i++) if (a[i] < x) result++; return result; É uma variante da função de contagem. A versão recursiva também é interessante: int ints_rank_general_r(const int *a, int n, int x) int result = 0; if (n > 0) result = (*a < x) + ints_rank_general_r(a+1, n-1, x); return result; Note bem: o valor aritmético das expressões lógicas é 0 ou 1. 11/3/16 Programação Imperativa 4

Arrays ordenados Um array está ordenado se o valor de cada elemento é menor ou igual ao valor do elemento seguinte. Essa propriedade é representada pela seguinte função lógica: int ints_is_sorted(int *a, int n); void unit_test_ints_is_sorted(void) int a[10] = 1,2,5,5,5, 6,8,8,9,9; assert(ints_is_sorted(a, 10)); assert(ints_is_sorted(a, 1)); assert(ints_is_sorted(a, 0)); int b[10] = 3,5,5,2,4, 4,8,8,2,5; assert(!ints_is_sorted(b, 10)); assert(ints_is_sorted(b, 3)); assert(!ints_is_sorted(b, 5)); assert(ints_is_sorted(b+3, 5)); assert(!ints_is_sorted(b+5, 5)); 11/3/16 Programação Imperativa 5

Função is_sorted Procura-se o primeiro par de elementos consecutivos fora de ordem: int ints_is_sorted(int *a, int n) for (int i = 1; i < n; i++) if (a[i-1] > a[i]) return 0; return 1; Também a versão recursiva: int ints_is_sorted_r(int *a, int n) return n <= 1 (a[0] <= a[1] && ints_is_sorted_r(a+1, n-1)); Um array está ordenado se o seu tamanho for menor ou igual a 1 ou, sendo maior que 1, se o valor do primeiro elemento for menor ou igual ao do segundo e o resto do array (tirando o primeiro elemento) estiver ordenado. 11/3/16 Programação Imperativa 6

Renque em arrays ordenados O renque geral inspeciona todos os elementos do array e conta aqueles que são menores que o valor dado. Se o array estiver ordenado, conseguimos calcular o renque sem inspecionar os elementos todos. Aliás, conseguimos fazê-lo inspecionando relativamente poucos elementos. Vejamos como. Por hipótese, temos um array a, ordenado, com tamanho n, e é dado um valor x: queremos calcular o número de elementos de a cujo valor é menor que x. 11/3/16 Programação Imperativa 7

Calculando o renque em arrays ordenados Tomemos um elemento qualquer de a, a[m]. Se x<=a[m], então x<=a[m+1], x<=a[m+2], etc., pois o array está ordenado; logo, todos os elementos de a cujo valor é menor que x estão à esquerda de a[m]. Inversamente, se x>a[m], então x>a[m-1], x>a[m-2], etc., porque o array está ordenado; logo, o valor de cada um dos elementos à esquerda de a[m] é menor que x e o valor de a[m] também. Sendo assim, no primeiro caso, basta contar os elementos de valor menor que x no subarray inicial com m elementos; no segundo, há pelo menos m+1 elementos com valor menor que x, a que se juntam os elementos menores que x no subarray a+(m+1). 11/3/16 Programação Imperativa 8

Função ints_rank, recursiva Veja com atenção: int ints_rank_r(const int *a, int n, int x) int result = 0; if (n > 0) int m = n / 2; if (x <= a[m]) result = ints_rank_r(a, m, x); else result = m+1 + ints_rank_r(a+m+1, n-(m+1), x); return result; Por uma questão de simetria, que convém computacionalmente, escolhemos o elemento a[m] a meio do array. 11/3/16 Programação Imperativa 9

Função ints_rank, iterativa Veja com muita atenção: int ints_rank(const int *a, int n, int x) int result = 0; while (n > 0) int m = n / 2; if (x <= a[m]) n = m; else result += m+1; a += m+1; n -= m+1; return result; Aqui, a contagem parcial mantém-se e o array encolhe, por assim dizer, pois a segunda metade não interessa. Aqui, entram na contagem os m+1 elementos à esquerda, pois são todos menores que x, e esses m+1 elementos são excluídos do processamento futuro, por assim dizer (pois já foram contabilizados). 11/3/16 Programação Imperativa 10

Busca dicotómica Queremos agora calcular o índice da primeira ocorrência de um valor dado num array ordenado, devolvendo -1, se não houver. Baseamo-nos no renque: int ints_bfind(const int *a, int n, int x) int r = ints_rank(a, n, x); return r < n && a[r] == x? r : -1; Se existirem no array ordenado elementos com valor x, eles virão todos de seguida e o primeiro deles estará na posição correspondente ao renque de x no array; inversamente, se o valor do elemento nessa posição não for igual a x, concluímos que não existe no array nenhum elemento com valor x. 11/3/16 Programação Imperativa 11

Complexidade da busca linear A busca linear, implementada pela função ints_find, precisa de inspecionar todos os elementos do array, no pior caso, isto é, quando o valor procurado não existe. Por isso, o trabalho computacional e, consequentemente, o tempo de execução são proporcionais ao tamanho do array, no pior caso. Para a busca linear, o melhor caso é aquele em que o elemento procurado é o que está na primeira posição, mas esse caso é contrabalançado por aquele em que o elemento procurado está na última posição. Portanto, se o array tiver 1000 elementos, por exemplo, então, nos casos em que o elemento procurado não existe, a comparação a[i] == x realizar-se-á 1000 vezes; nos casos em que existe, realizar-se-á, em média, 500 vezes. 11/3/16 int ints_find(const int *a, int n, int x) for (int i = 0; i < n; i++) if (a[i] == x) return i; return -1; Programação Imperativa 12

Complexidade da busca dicotómica Na busca dicotómica, o tamanho do array em observação é dividido ao meio, sensivelmente, em cada passo do ciclo. Por exemplo, se o array tiver 1000 elementos, após o primeiro passo do ciclo só nos interessa um subarray com 500 elementos; após o segundo passo, só nos interessa o subarray com 250 elementos; depois 125, 62, 31, 15, 8, 4, 2, 1. Quer dizer: o ciclo while terá dado 10 voltas e a comparação x <= a[m] terá sido realizada 10 vezes. Conclusão: a busca dicotómica é muito melhor que a busca linear. No entanto, só dá com array ordenados. 11/3/16 Programação Imperativa 13

Complexidade logarítmica Portanto, em geral, o tempo usado para encontrar um elemento no array, ou decidir que ele não existe, é proporcional ao logaritmo do número de elementos: T = K * log 2 N. Por exemplo, se uma busca dicotómica num array com 1000 elementos demorar 200 nanossegundos, num array de 2000 elementos demorará 220 nanossegundos (log 2 1000 10, log 2 2000 11) e num array de 1000000 elementos demorará 400 nanossegundos (log 2 1000000 20). Dizemos que a busca dicotómica tem complexidade logarítmica. A busca linear tem complexidade linear. 11/3/16 Programação Imperativa 14

Busca dicotómica clássica Frequentemente, a busca dicotómica é programada delimitando o intervalo de busca, por meio de dois índices. Em cada passo, o intervalo de busca encolhe para pouco menos de metade: int ints_bfind_classic(const int *a, int n, int x) int i = 0; Em cada passo, o intervalo de busca é delimitado pelos int j = n-1; valores das variáveis i e j. while (i <= j) int m = i + (j-i) / 2; if (x < a[m]) j = m-1; else if (x > a[m]) i = m+1; else return m; return -1; Note bem: esta função só é equivalente à outra no caso em que os arrays não tem valores duplicados. Para arrays com valores duplicados, a outra dá a posição da primeira ocorrência enquanto esta dá a posição de uma ocorrência, não necessariamente a primeira. 11/3/16 Programação Imperativa 15

Método da bisseção A ideia de dividir ao meio o intervalo de busca em cada passo ocorre em vários problemas. Por exemplo: calcular a raiz quadrada de um número positivo, x, maior que 1. A raiz existe no intervalo entre 1 e x. Observamos o ponto médio do intervalo, m. Se m*m for igual a x, ou estiver muito perto de x, tomamos m como raiz quadrada de x. Se não, se m*m for maior que x, a raiz existirá no intervalo de 1 a m; se m*m for menor que x, a raiz existirá no intervalo de m a x. Etc. 11/3/16 Programação Imperativa 16

Método da bisseção, recursivo Calcular recursivamente a raiz de x no intervalo de a a b: double square_root_r(double x, double a, double b) assert(x > 1); assert(a < b); assert(a*a - x < 0), assert(b*b - x > 0); double result = (a + b) / 2; if (fabs(result * result - x) >= 0.000001) if (result * result < x) result = square_root_r(x, result, b); else result = square_root_r(x, a, result); return result; 11/3/16 Programação Imperativa 17

Método da bisseção, iterativa A versão iterativa deriva-se diretamente da anterior: double square_root_i(double x, double a, double b) assert(x > 1); assert(a < b); assert(a*a - x < 0), assert(b*b - x > 0); double result = (a + b) / 2; while (fabs(result * result - x) >= 0.000001) if (result * result < x) a = result; else b = result; result = (a + b) / 2; return result; 11/3/16 Programação Imperativa 18

Função principal, raiz quadrada Para demonstração, a função principal invoca ou a versão iterativa ou a versão recursiva: double square_root(double x) assert(x > 0); double result; if (x > 1) result = square_root_r(x, 1.0, x); // result = square_root_i(x, 1.0, x); else if (x < 1) result = 1 / square_root(1/x); else result = 1; return result; A raiz de números menores que 1 calcula-se invertendo a raiz do inverso (o qual será maior que 1...) 11/3/16 Programação Imperativa 19

Função de teste, raiz quadrada void test_square_root(void) double x; while (scanf("%lf", &x)!= EOF) double z = square_root(x); printf("%.6f\n", z); Note bem: o método da bisseção é interessante mas há outros métodos melhores para calcular a raiz quadrada e, mais geralmente, para resolver equações não lineares. Esses métodos convergem mais depressa para a solução, pois dividem o intervalo de busca mais agressivamente. $./a.out 6 2.449490 25 5.000000 0.01 0.100000 2 1.414214 0.5 0.707107 64 8.000000 1.44 1.200000 $ 11/3/16 Programação Imperativa 20