Medida do tempo de execução de um programa

Livre

0
0
58
1 year ago
Preview
Full text

  Medida do tempo de execução de um programa

  

Algoritmos e Estruturas de Dados I

nataliabatista@decom.cefetmg.br https://sites.google.com/site/nataliacefetmg/ Natália Batista

   Custo de utilização de um algoritmo: tempo de execução quantidade de operações executadas

  quantidade de memória

   Tempo de execução vantagens

  desvantagens

   Objeções:

resultados são dependentes do compilador que pode

favorecer algumas construções em detrimento de outras; diferenças de hardware;

quando grande quantidade de memória é utilizada, as

medidas de tempo podem depender deste aspecto.

  

  E quando existem vários algoritmos para resolver o mesmo problema, todos com custo de execução na mesma ordem de grandeza?

  

  Uma forma mais adequada de medir o custo e utilização de um algoritmo é usando um

  modelo matemático baseado em um computador idealizado.

   Modelo computacional.

  

  Modelo: Esquema que possibilita a representação de uma entidade (Houaiss). No modelo, só se deve incluir o que for relevante para a modelagem do objeto em questão.

  

  Computacional: Relativo ao processamento (Houaiss).

  

  Definição (nosso contexto): Esquema que descreve como é o modelo abstrato do processamento de algoritmos.

  

  Importância: Um algoritmo não existe, ou seja, não é possível escrevê-lo, se antes não for definido o modelo computacional associado (como será executado).

  

  Conceito básico no projeto de qualquer algoritmo.

  

  O mais popular (usado) de todos: RAM – Random Access Machine. Modela o computador tradicional e outros elementos computacionais.

   Elementos do modelo:

   um único processador; memória. Observações:

  Podemos ignorar os dispositivos de entrada e saída

(teclado, monitor, etc) assumindo que a codificação do

algoritmo e os dados já estão armazenados na memória.

  Computação nesse modelo: Processador busca instrução/dado da memória. Uma única instrução é executada de cada vez. Cada instrução é executada seqüencialmente.

  Modelo RAM

  

  Para medir o custo de execução de um algoritmo define-se uma função de custo ou

  f função de complexidade .

   f(n) é a medida do tempo necessário para

  executar um algoritmo para um problema de tamanho n.

  

  Se f é a medida da quantidade de tempo, então f é função de complexidade de tempo .

  

  Se f é a medida da quantidade de memória, então f é função de complexidade de espaço .

  

  No livro e na disciplina: complexidade de tempo.

  

  A complexidade de tempo não representa tempo diretamente, mas o número de vezes que determinada operação considerada relevante é executada.

  

  A complexidade de espaço representa a quantidade de memória que é necessária para armazenar as estruturas de dados associadas ao algoritmo.

  

  Exemplo: algoritmo para encontrar o maior elemento de um vetor de inteiros A[1..n], n1. int i, Temp; int Max (int A[], int n){ for (i = 1; i < n; i = i + 1) Temp = A[0]; return Temp; if (Temp < A[i]) Temp = A[i]; }

  

  Seja f uma função de complexidade tal que f(n) é o número de comparações entre os elementos de A, se A contiver n elementos. Logo, f(n) = n – 1, para n>0.

  

  O algoritmo anterior é ótimo?

  Quando o custo de um algoritmo é igual ao menor custo possível, então o algoritmo é ótimo para a (Ziviani) medida de custo considerada.

   Teorema: qualquer algoritmo para encontrar

  o maior elemento de um conjunto com n elementos, n1, faz pelo menos n-1 comparações.

  

  Medida do custo de execução de um algoritmo depende: do tamanho da entrada dos dados ou de uma entrada particular dos dados

   Três cenários: melhor caso pior caso

  caso médio

  

  Melhor caso Menor tempo de execução sobre todas as entradas de tamanho n.

   Pior caso

  Maior tempo de execução sobre todas as entradas de tamanho n.

  Se f é uma função de complexidade baseada na análise de pior caso, o custo de aplicar o algoritmo nunca é maior do que f(n).

   Caso médio (ou caso esperado)

  Média dos tempos de execução de todas as entradas de tamanho n.

  Uma distribuição de probabilidades sobre o conjunto de entradas de tamanho n é suposta, por exemplo uniforme.

  

  Considere o problema de acessar os registros de um arquivo.

  

  Cada registro contém uma chave única que é utilizada para recuperar registros do arquivo.

  

  O problema: dada uma chave qualquer, localize o registro que contenha esta chave.

  

  O algoritmo de pesquisa mais simples é o que faz a pesquisa seqüencial.

  

  Seja f uma função de complexidade tal que f(n) é o número de registros consultados no arquivo (número de vezes que a chave de consulta é comparada com a chave de cada registro).

  

  Melhor caso, pior caso, caso médio?

   Melhor caso:

  f(n) = 1

   Pior caso:

  f(n) = n

   Caso médio:

  f(n) = (n+1)/2

  

  No estudo do caso médio, vamos considerar que toda pesquisa recupera um registro.

  Se p for a probabilidade de que o i-ésimo registro i seja procurado e, considerando que para recuperar o i-ésimo registro são necessárias i comparações, então: f(n) = 1 x p +2 x p +3 x p + ... +n x p . 1 2 3 n

  

  Para calcular f(n) basta conhecer a distribuição de probabilidades p . i

  

  Se cada registro tiver a mesma probabilidade de ser acessado que todos os outros, então p i = 1/n,1i n.

  Neste caso:

  

  A análise do caso esperado revela que uma pesquisa com sucesso examina aproximadamente metade dos registros.

   Algoritmo para encontrar o maior e o menor

elementos de um vetor de inteiros A[1..n], n1.

int i; int MaxMin1 (int A[], int *Max, int *Min, int n){ for (i = 1; i < n; i = i + 1){ *Max = A[0]; *Min = A[0]; if (A[i] < *Min) *Min = A[i]; if (A[i] > *Max) *Max = A[i]; } }

  

  Seja f(n) o número de comparações entre os elementos de A, se A tiver n elementos.

  

  Logo f(n) = 2(n - 1), para n > 0, para o melhor caso, pior caso e caso médio.

  

  Pode-se melhorar MaxMin1?

  

  Observe que a comparação A[i] < Min somente é necessária quando o resultado da comparação A[i] > Max é falso.

   int i; int MaxMin2 (int A[], int *Max, int *Min, int n){ for (i = 1; i < n; i = i + 1){ *Max = A[0]; *Min = A[0]; if (A[i] > *Max) *Max = A[i]; else if (A[i] < *Min) *Min = A[i]; } }

   Melhor caso: f(n) = n-1 o vetor A está em ordem crescente

  os comandos do else nunca serão executados

   Pior caso: f(n) = 2(n-1) o vetor A está ordem decrescente

  em todas as comparações, A[i] perde para Max

  

  Caso médio: A[i] é maior do que Max a metade das vezes. Sempre faz a comparação do primeiro if: n-1 Metade das vezes executa o else: (n-1)/2

  

  Considerando o número de comparações realizadas, existe a possibilidade de obter um algoritmo mais eficiente para este problema?

  

  Sim! Particionando o vetor em dois subconjuntos.

  (Ziviani) separando-os em dois subconjuntos (maiores em um e menores em outro), a um custo de ⌈ n/2 ⌉ 2. comparações.

  O máximo é obtido do subconjunto que contém os

  ⌈ ⌉

  maiores elementos, a um custo de n/2 - 1 3. comparações.

  O mínimo é obtido do subconjunto que contém os

  ⌈ ⌉

  menores elementos, a um custo de n/2 - 1 comparações.

  

  Implementação: quando n é ímpar, o elemento que está na posição A[n-1] é duplicado na posição A[n] para evitar tratamento de exceção.

   } A[n] = A[n-1]; FimDoAnel = n; if ((n % 2) > 0){ //se n é ímpar, duplica último elemento if (A[0] > A[1]){ else FimDoAnel = n - 1; else { } *Max = A[0]; *Min = A[1]; } *Max = A[1]; *Min = A[0]; i = 3; if (A[i-1] > *Max) *Max = A[i-1]; if (A[i-1] > A[i]){ while (i <= FimDoAnel){ else { } if (A[i] < *Min) *Min = A[i]; } if (A[i] > *Max) *Max = A[i]; if (A[i-1] < *Min) *Min = A[i-1]; i = i + 2;

  

  Comparação dos elementos aos pares: n/2 Obtenção do máximo do subconjunto dos maiores elementos: n/2-1

  Obtenção do mínimo do subconjunto dos menores elementos: n/2-1 Para o melhor caso, pior caso e caso médio:

   Comparação dos algoritmos para obter o máximo e o mínimo:

  • Os algoritmos MaxMin2 e MaxMin3 são superiores ao algoritmo MaxMin1 de forma geral. O algoritmo MaxMin3 é superior ao algoritmo MaxMin2 com relação ao pior caso e bastante próximo quanto ao caso médio.

  

  É possível obter um algoritmo MaxMin mais eficiente?

  

  Para responder temos de conhecer o limite inferior para essa classe de algoritmos.

   Técnica utilizada: uso de um oráculo.

  

  De acordo com o dicionário Aurélio, um 1. oráculo é: 2. Resposta de um deus a quem o consultava.

  Divindade que responde consultas e orienta o 3. crente: o oráculo de Delfos.

  Palavra, sentença ou decisão inspirada, infalível ou que tem grande autoridade: os oráculos dos profetas, os oráculos da ciência.

  

  Dado um modelo de computação que expresse o comportamento do algoritmo, o oráculo informa o resultado de cada passo possível (no caso, o resultado de cada comparação).

  

  O teorema a seguir utiliza um oráculo para derivar o limite inferior no número de comparações necessárias para o problema do máximo e do mínimo.

  

  Teorema: Qualquer algoritmo para encontrar o maior e o menor elementos de um conjunto com n elementos não ordenados, n  1, faz

  ⌈ n/2 ⌉ - 2 pelo menos 3 comparações.

  

  Prova: A técnica utilizada define um oráculo que descreve o comportamento do algoritmo por meio de

   um conjunto de n–tuplas, e um conjunto de regras associadas que mostram as tuplas possíveis (estados) que um algoritmo pode assumir a partir de uma dada tupla e uma única comparação.

  

  Uma 4–tupla, representada por ( a , b , c , d ), onde os elementos de:

   a nunca foram comparados; b foram vencedores e nunca perderam em

   comparações realizadas;

  c foram perdedores e nunca venceram em

   comparações realizadas;

  d foram vencedores e perdedores em comparações realizadas.

  

  O algoritmo inicia no estado (n, 0, 0, 0) e termina com (0, 1, 1, n - 2).

  

  Após cada comparação a tupla (a, b, c, d) consegue progredir apenas se ela assume um dentre os seis estados possíveis abaixo:

  

  Dadas as transições de estado, é possível derivar o limite inferior determinando-se que o estado final não pode ser atingido usando um número menor de transições.

   O primeiro passo requer necessariamente a manipulação do componente a.

   O caminho mais rápido para levar a até zero requer ⌈n/2⌉ mudanças de estado e termina com a tupla (0, n/2, n/2, 0) (comparação dos elementos de a dois a dois).

  

A seguir, para reduzir o componente b até um

  • - 1 mudanças de estado

  são necessárias ⌈n/2⌉ (mínimo de comparações para obter o maior elemento de b).

  

  • - 1 mudanças de

  Idem para c, com ⌈n/2⌉ estado.

  

  Logo, para obter o estado (0, 1, 1, n - 2) a partir do estado (n, 0, 0, 0) as comparações necessárias são:

  

  O teorema nos diz que se o número de comparações entre os elementos de um vetor for utilizado como medida de custo, então o algoritmo MaxMin3 é ótimo.

   for (i=0; i<n-1; i++){ int i, j;

  Calcule o número de int min = i; trocas/comparações for (j = i+1; j < n; j++){ que o algoritmo de if (v[j] < v[min]){ ordenação por seleção min = j; realiza no pior caso. int x = v[min]; //troca v[min] e v[i] v[i] = x; v[min] = v[i]; } }

}

}

  

  Considerando somente as comparações entre elementos do vetor: O for mais externo é executado de 0 a n-2, ou seja, n-1 vezes. O for mais interno é executado de i+1 a n-1, ou seja, n-1 - (i+1) + 1 = n - i - 1 vezes. Como se trata do pior caso, iremos assumir que a comparação do if será sempre executada. Logo n f(n)

  1

  2

  1

  3

  3 ... ... 2 n (n -n)/2

  

  Nivio Ziviani. Projeto de algoritmos: com implementações em Pascal e C. 3 ed.

  Editora Cengage Learning, 2011.

  

  Antonio Alfredo Ferreira Loureiro. Projeto e

  Análise de Algoritmos: Análise de Complexidade. Notas de aula, 2010.

Novo documento

Tags