sábado, 26 de novembro de 2016

Análise de Algoritmos

Segundo Thomas Cormen, analisar um algoritmo significa prever os recursos de que ele necessitará, como memória, largura de banda, hardware, e principalmente, tempo de execução.
Alguns algoritmos podem aumentar seu tempo de execução de acordo com o tamanho da sua entrada. Por exemplo, no algoritmo de ordenação, quanto mais elementos tiver a entrada, mais irá demorar.
O tamanho da entrada é o número total de itens na entrada. Por exemplo, o tamanho de um arranjo que será ordenado.
O tempo de execução é o número de operações primitivas ou etapas executadas.
Para calcular o tempo de execução de um algoritmo, existe uma fórmula complicada de explicar, que ao final se torna uma fórmula simples. Primeiro apresenta-se o custo de tempo de cada instrução (que, apesar de na prática não ser assim, fica convencionado que esse custo é um valor constante), e em seguida o número de vezes que cada instrução é executada. Assim, o tempo de execução é a soma dos tempos de execução para cada instrução executada; uma instrução que demanda ci passos para ser executada, e é executada n vezes, contribuirá com cin para o tempo total da execução. Esse tempo total é a soma dos produtos das colunas custos e quantidade de vezes (c1n + c2n + cnn).
Esse cálculo poderá resultar em uma função linear de n (an + b), onde a e b depende do dos custos de instrução ci, representando o melhor caso de tempo de execução (exemplo: executar o algoritmo de ordenação em um arranjo já ordenado). Esse mesmo cálculo também poderá resultar em uma função quadrática de n (an2 + bn + c), onde a, b e c depende do dos custos de instrução ci, representando o pior caso de tempo de execução (exemplo: executar o algoritmo de ordenação em um arranjo ordenado em ordem decrescente).
Apesar do cálculo complexo explicado anteriormente, existe uma forma mais simples de medir a eficiência de um algoritmo, através do cálculo da taxa ou ordem de crescimento do tempo de execução. Para fazer esse cálculo, basta selecionar o termo inicial da fórmula de tempo de execução do pior caso do algoritmo (no exemplo anterior: an2), e desconsiderar deste apenas o coeficiente constante, tendo em vista que fatores constantes são menos significativos que a taxa de crescimento na determinação da eficiência computacional para grandes entradas. Portanto, o algoritmo possui ordem de crescimento igual q(n2).

Exercícios (Livro Algoritmos Teoria e Prática – Thomas Cormen 2012):

1-     Expresse a função n3/1000 - 100n2 - 100n + 3 em termos da notação q.
n3

2-     Considere a ordenação de n números armazenados no arranjo A, localizando primeiro o menor elemento de A e permutando esse elemento com o elemento contido em A[1]. Em seguida, encontre o segundo menor elemento de A e o troque pelo elemento A[2]. Continue dessa maneira para os primeiros n - 1 elementos de A. Escreva o pseudocódigo para esse algoritmo, conhecido como ordenação por seleção. Que loop invariante esse algoritmo mantém? Por que ele só precisa ser executado para os primeiros n - 1 elementos, e não para todos os n elementos?
OrdenacaoSelecao(A)
para j ¬ 1 até comprimento(A) - 1 faça
            i ¬ j + 1
            menor ¬ A[j]
            temp ¬  A[j]
            posicao ¬ j
            enquanto i  <= comprimento(A) faça
                        se A[i] < menor então
                                   menor ¬ A[i]
                                   posicao ¬ i
                        i ¬ i + 1
            A[j] ¬ menor
            A[posicao] ¬ temp

O algoritmo só precisa ser executado até n – 1 por causa da permuta de valores no arranjo; ao chegar ao elemento n -1, o mesmo só poderá ser permutado com o elemento n, o qual não terá mais elementos com o que ser comparado.

3-     Considere o problema de pesquisa: Entrada: Uma sequência de n números A = (a1, a2, ..., an) e um valor v. Saída: Um índice i tal que v = A[i] ou o valor especial NIL, se v não aparecer em A. Quantos elementos da sequência de entrada precisam ser verificados em média, supondo-se que o elemento que está sendo procurado tenha a mesma probabilidade de ser qualquer elemento no arranjo? E no pior caso? Quais são os tempos de execução do caso médio e do pior caso da pesquisa linear em notação q? Justifique suas respostas.
Todos os elementos precisam ser testados até o final do arranjo. Dado que o algoritmo apresenta apenas um loop, teríamos no pior dos casos o tempo de execução qn2.

4-     Como podemos modificar praticamente qualquer algoritmo para ter um bom tempo de execução no melhor caso?

Inserindo a melhor opção de entrada. No caso de um algoritmo de ordenação a melhor entrada é a de um arranjo já ordenado.

quinta-feira, 24 de novembro de 2016

Ordenação por Inserção

Algoritmos são escritos por meio de uma linguagem chamada pseudocódigo. Esta linguagem é semelhante em alguns aspectos a linguagens de programação de alto nível, o que diverge é que o pseudocódigo é o mais próximo possível da linguagem comum, de forma a apresentar a informação o mais clara possível.
Um algoritmo possui uma entrada (que são os dados iniciais do problema), e uma saída (que são os dados processados pelo algoritmo, o resultado).
Ordenação por inserção é um exemplo de algoritmo. É utilizado para ordenar de maneira eficiente um número pequeno de elementos. Funciona semelhante à maneira como as pessoas ordenam cartas na mão em jogos de baralho: as cartas são empilhadas na mesa, enquanto a mão está vazia; a primeira carta é retirada e colocada na mão; em seguida uma carta de cada vez é removida da mesa, comparando-a com cada carta da mão, da direita para a esquerda, encaixando na posição de ordenação correta, e realizando assim a ordenação.
O pseudocódigo do algoritmo de ordenação por inserção é chamado de insertion-sort. Para melhor entendimento, ele pode ser visualizado como um arranjo de sequência. Inicialmente é definido o comprimento do arranjo – [A], que corresponde ao número de elementos a serem ordenados – comprimento[A]. Assim, os números são ordenados no local, dentro do arranjo, ficando sempre no máximo um número fora. Ao final, o mesmo arranjo conterá a sequência ordenada. A figura abaixo demonstra isso:


Abaixo o algoritmo escrito:

AlgoritmoOrdenaçãoInsercao(A)
para j ¬ 2 até comprimento[A] faça
            chave ¬ A[j]
            i ¬ j -1
            enquanto A[i] > chave e i > 0 faça
                        A[i + 1] ¬ A[i]
                        i ¬ i -1
            A[i + 1] ¬ chave

O conceito de loop invariante ajuda a analisar programas, verificar erros, e atestar a coerência do algoritmo. O mesmo possui três propriedades: Inicialização (o loop é verdadeiro antes da primeira interação), Manutenção (se o loop for verdadeiro antes da primeira interação, então permanecerá verdadeiro antes da próxima interação), e Término (quando o loop termina, o invariante aponta uma propriedade útil que mostra que o algoritmo está correto).
Tomando como exemplo o algoritmo de ordenação por inserção, é possível verificar que o mesmo possui a primeira propriedade (Inicialização), pois o loop é válido antes da primeira de iniciar, a atribuição à variável é realizada. Este algoritmo também possui a segunda propriedade (Manutenção), já que o loop continua invariante à medida que ocorre cada interação e a ordenação começa a aparecer. A terceira propriedade (Término) também pode ser vista, dado que ao final da última interação, o arranjo inteiro é ordenado; o que significa que o algoritmo está correto.

Exercícios (Livro Algoritmos Teoria e Prática – Thomas Cormen 2012):

1-     Usando a Figura acima como modelo, ilustre a operação de INSERTION-SORT no arranjo A = (31, 41, 59, 26, 41, 58).
  


2-     Reescreva o procedimento INSERTION-SORT (OrdenaçãoInserção) para ordenar em ordem não crescente, em vez da ordem não decrescente.
. AlgoritmoOrdenaçãoInsercao(A)
para j ¬ 2 até comprimento[A] faça
            chave ¬ A[j]
            i ¬ j -1
            enquanto A[i] < chave e i > 0 faça
                        A[i + 1] ¬ A[i]
                        i ¬ i -1
            A[i + 1] ¬ chave

3-     Considere o problema de pesquisa: Entrada: Uma sequência de n números A = (a1, a2, ..., an) e um valor v. Saída: Um índice i tal que v = A[i] ou o valor especial NIL, se v não aparecer em A. Escreva o pseudocódigo para pesquisa linear, que faça a varredura da sequência, procurando por v. Usando um loop invariante, prove que seu algoritmo é correto. Certifique-se de que seu loop invariante satisfaz às três propriedades necessárias.
Pesquisa(A, v)
i ¬ NIL
para j ¬ 1 até comprimento(A) faça
            se v = A[j] então
                        i = A[j]
escreva(i)

4-     Considere o problema de somar dois inteiros binários de n bits, armazenados em dois arranjos de n elementos A e B. A soma dos dois inteiros deve ser armazenada em forma binária em um arranjo de (n + 1) elementos C. Enuncie o problema de modo formal e escreva o pseudocódigo para somar os dois inteiros.
Escreva o algoritmo para a soma de dois inteiros binários de n bits, os quais estão armazenados em dois arranjos (A e B) em forma binária, onde o resultado deve ser armazenado em forma binária no arranjo C de n + 1 elementos.
SomaBinarios(A, B)
C: vetor[comprimento(A) + 1]
resto = 0
resultado = 0
para j ¬ comprimento(C)  até 1 faça
resultado = resto + A[j] + B[j]
            se resultado > 1 então
                        se resultado = 2 então
                                    resultado = 0
                                   resto = 1
                        senão  
                                   resultado = 1
                                   resto = 1
            senão
                        resto = 0
C[0] = resto

escreva(i)

quarta-feira, 23 de novembro de 2016

Algoritmos e Outras Tecnologias

Os algoritmos eficientes em termos de tempo ou espaço ajudam a diminuir o custo dos computadores, dada à quantidade finita de velocidade e memória dos computadores. É por esse motivo que o estudo dessa tecnologia é tão importante quanto qualquer outra, devido a sua usabilidade em quase todos os tipos de sistemas existentes.

Exercícios (Livro Algoritmos Teoria e Prática – Thomas Cormen 2012):

1-     Forneça um exemplo de aplicação que exige conteúdo algorítmico no nível da aplicação e discuta a função dos algoritmos envolvidos.
Uma página de busca de hotéis utiliza algoritmos de busca, comparação e ordenação para apresentar as melhores opções para seus clientes no topo da página.

2-     Vamos supor que estamos comparando implementações de ordenação por inserção e ordenação por intercalação na mesma máquina. Para entradas de tamanho n, a ordenação por inserção é executada em 8n2 etapas, enquanto a ordenação por intercalação é executada em 64n Ig n etapas. Para que valores de n a ordenação por inserção supera a ordenação por intercalação?
Tabela contendo os possíveis valores de n até que se encontre o ponto onde a ordenação por inserção supera a ordenação por intercalação:
Valor de n
 Inserção (8n2)
Intercalação (64n Ig n)
1
                   8,00
                                      -  
2
                 32,00
                             128,00
3
                 72,00
                             304,31
4
              128,00
                             512,00
5
              200,00
                             743,02
6
              288,00
                             992,63
7
              392,00
                          1.257,70
8
              512,00
                          1.536,00
9
              648,00
                          1.825,88
10
              800,00
                          2.126,03
11
              968,00
                          2.435,44
12
           1.152,00
                          2.753,25
13
           1.352,00
                          3.078,77
14
           1.568,00
                          3.411,39
15
           1.800,00
                          3.750,61
16
           2.048,00
                          4.096,00
17
           2.312,00
                          4.447,16
18
           2.592,00
                          4.803,75
19
           2.888,00
                          5.165,48
20
           3.200,00
                          5.532,07
21
           3.528,00
                          5.903,27
22
           3.872,00
                          6.278,88
23
           4.232,00
                          6.658,68
24
           4.608,00
                          7.042,50
25
           5.000,00
                          7.430,17
26
           5.408,00
                          7.821,53
27
           5.832,00
                          8.216,45
28
           6.272,00
                          8.614,78
29
           6.728,00
                          9.016,41
30
           7.200,00
                          9.421,23
31
           7.688,00
                          9.829,13
32
           8.192,00
                       10.240,00
33
           8.712,00
                       10.653,76
34
           9.248,00
                       11.070,32
35
           9.800,00
                       11.489,59
36
         10.368,00
                       11.911,51
37
         10.952,00
                       12.335,99
38
         11.552,00
                       12.762,96
39
         12.168,00
                       13.192,36
40
         12.800,00
                       13.624,14
41
         13.448,00
                       14.058,22
42
         14.112,00
                       14.494,55
43
         14.792,00
                       14.933,08


3-     Qual é o menor valor de n tal que um algoritmo cujo tempo de execução é 100n2 funciona mais rápido que um algoritmo cujo tempo de execução é 2n na mesma máquina?
Tabela contendo os possíveis valores de n até que se encontre o ponto onde o algoritmo A supera (ou seja, possui menor velocidade) que o algoritmo B.
Valor de n
Algoritmo A (100n2)
Algoritmo B (2n)
1
                       100,00
                     2,00
2
                       400,00
                     4,00
3
                       900,00
                     8,00
4
                    1.600,00
                  16,00
5
                    2.500,00
                  32,00
6
                    3.600,00
                  64,00
7
                    4.900,00
                128,00
8
                    6.400,00
                256,00
9
                    8.100,00
                512,00
10
                 10.000,00
             1.024,00
11
                 12.100,00
             2.048,00
12
                 14.400,00
             4.096,00
13
                 16.900,00
             8.192,00
14
                 19.600,00
          16.384,00
15
                 22.500,00
          32.768,00

Problemas (Livro Algoritmos Teoria e Prática – Thomas Cormen 2012):

1-     Comparação entre tempos de execução: Para cada função f(n) e cada tempo t na tabela a seguir, determine o maior tamanho n de um problema que pode ser resolvido no tempo t, supondo-se que o algoritmo para resolver o problema demore f(n) microssegundos.
Tabela contendo os possíveis valores de n.

1 segundo
1 minuto
1 hora
1 dia
1 mês
1 ano
1 século
lg n
            9,97 
        15,87 
              21,78 
                26,36 
                    31,27
                      34,86
                            41,50
n
          31,62
      244,95
        1.897,37 
          9.295,16 
           50.911,69
           176.363,26
              1.763.632,61
n
1.000
60.000
 3.600.000
86.400.000
2.592.000.000
31.104.000.000
3.110.400.000.000
.