Fundamentos da Programação - ULisboa

Post on 19-Oct-2021

4 views 0 download

Transcript of Fundamentos da Programação - ULisboa

Fundamentos da Programação

Capítulo 5: Listas

Algoritmos de procura• Procura de informação

• É uma das nossas atividades quotidianas• Procuramos páginas com determinadas palavras no Google• Livros em bibliotecas• Produtos em supermercados, etc.

• É uma das atividades importantes num sistema computacional• Parece ser muito simples, a eficiência levanta problemas

• Procura da palavra “Python”' no Google: devolve cerca de 312 milhões de resultados em 0.57 segundos

Algoritmos de procura• A procura é frequentemente executada recorrendo a listas• Função procura, recebe como argumentos uma lista lst e um elemento (a chave)

• Devolve o índice do elemento da lista cujo valor é igual a chave• Convencionamos que, se chave não pertencer à lista, a função devolve -1

Algoritmos de procuraProcura sequencial

Algoritmos de procuraProcura sequencial

def procura(lst, chave):

for i in range(len(lst)):

if lst[i] == chave:

return i

return -1

Algoritmos de procuraProcura bináriaA procura sequencial pode exigir a inspeção de todos os elementos da lista

A procura binária é mais eficiente, exigindo que os elementos da lista encontrem ordenados

• Consideramos o elemento que se encontra no meio da lista• Se for igual ao que estamos a procurar, então a procura termina• Se é menor, o elemento procurado não se encontra na primeira metade da lista. Repetimos

o processo para a segunda metade da lista• Se é maior, o elemento procurado não se encontra na segunda metade da lista. Repetimos

processo para a primeira metade da lista

Em cada passo, o número de elementos a considerar é reduzido para metade

Algoritmos de procuraProcura binária

2 5 9 10 11 20Procurar

0 1 2 3 4 5

linf lsupmeio

10

2 5 9 10 11 20

0 1 2 3 4 5

linf lsup

meio

2 5 9 10 11 20

0 1 2 3 4 5

linf,lsup,meio

Algoritmos de procuraProcura binária 2 5 9 10 11 20

Procurar

0 1 2 3 4 5

linf lsupmeio

12

2 5 9 10 11 20

0 1 2 3 4 5

linf lsup

meio

2 5 9 10 11 20

0 1 2 3 4 5

linf,lsup,meio

2 5 9 10 11 20

0 1 2 3 4 5

linflsup

Algoritmos de procuraProcura bináriadef procura(lst, chave):

linf = 0lsup = len(lst) - 1while linf <= lsup:

meio = (linf + lsup) // 2if chave == lst[meio]:

return meioelif chave > lst[meio]:

linf = meio + 1else:

lsup = meio - 1return -1

Algoritmos de ordenação• Uma lista ordenada facilita a procura

• Procura sequencial vs. procura binária

• Na nossa vida quotidiana, ordenamos as coisas para tornar a procura mais fácil, e não porque somos arrumados

• Supermercado sem produtos agrupados• Biblioteca sem livros ordenados

• Estudo de alguns algoritmos para ordenar os valores contidos numa lista

Algoritmos de ordenaçãoOrdenação por borbulhamento• Percorre os elementos a ordenar, comparando elementos adjacentes, trocando

os pares de elementos que se encontram fora de ordem• De um modo geral, uma única passagem pela sequência de elementos não

ordena a lista, pelo que é necessário efetuar várias passagens• A lista encontra-se ordenada quando se efetua uma passagem completa em

que não é necessário trocar a ordem de nenhum elemento

Algoritmos de ordenaçãoOrdenação por borbulhamento

Algoritmos de ordenaçãoOrdenação por borbulhamento

def ordena(lst):maior_indice = len(lst) - 1 nenhuma_troca = False # garante que o ciclo while é executadowhile not nenhuma_troca:

nenhuma_troca = Truefor i in range(maior_indice):

if lst[i] > lst[i+1]:lst[i], lst[i+1] = lst[i+1], lst[i]nenhuma_troca = False

maior_indice = maior_indice - 1

Algoritmos de ordenaçãoOrdenação por seleção• Percorre os elementos a ordenar e, em cada passagem, coloca um elemento

na sua posição correta• Na primeira passagem, coloca-se o menor elemento na sua posição correta,

na segunda passagem, o segundo menor, e assim sucessivamente

Algoritmos de ordenaçãoOrdenação por seleção

Algoritmos de ordenaçãoOrdenação por seleçãodef ordena(lst):

for i in range(len(lst)):

pos_menor = i

for j in range(i + 1, len(lst)):

if lst[j] < lst[pos_menor]:

pos_menor = j

lst[i], lst[pos_menor] = lst[pos_menor], lst[i]

Ordenação usando listas de índices• Os algoritmos de ordenação que apresentámos efetuam a ordenação de forma

destrutiva• A lista inicial é alterada para a lista ordenada

• Uma alternativa consiste em criar uma lista auxiliar contendo os índices (posições na lista original) dos elementos ordenados, não alterando a lista original mas apenas a lista dos índices

• Permite manter várias “vistas” ordenadas sobre a mesma lista• Exemplo informação sobre os voos de um aeroporto

Ordenação usando listas de índicesOrdenação por borbulhamento

Ordenação usando listas de índicesOrdenação por borbulhamento

Ordenação usando listas de índicesOrdenação por borbulhamento

def ordena(lst):maior_indice = len(lst) - 1ind = list(range(len(lst)))nenhuma_troca = Falsewhile not nenhuma_troca:

nenhuma_troca = Truefor i in range(maior_indice):

if lst[ind[i]] > lst[ind[i+1]]:ind[i], ind[i+1] = ind[i+1], ind[i]nenhuma_troca = False

maior_indice = maior_indice - 1 return ind

Ordenação usando listas de índicesOrdenação por borbulhamento

def devolve_ordenada(lst, indices):res = [] # vamos construir a lista ordenadafor i in range(len(lst)):

res = res + [lst[indices[i]]]return res

Considerações sobre eficiência• A procura binária é mais eficiente do que a procura sequencial

• O que é que isto significa?

• Métodos para comparar a eficiência de algoritmos• Eficiência de um algoritmo• Uma medida grosseira do trabalho envolvido na execução de um

algoritmo

Considerações sobre eficiência• O trabalho envolvido na execução de um algoritmo poder variar drasticamente

com os dados fornecidos• Se a lista estiver ordenada, a ordenação por borbulhamento só necessita de uma

passagem

• Encontrar uma medida do trabalho que seja independente dos valores particulares dos dados utilizados na sua execução

• Ordem de crescimento avaliação grosseira dos recursos exigidos pelo algoritmo à medida que a quantidade de dados manipulados aumenta

• Isolar uma operação que seja fundamental para o algoritmo, e contar o número de vezes que esta operação é executada

• Para o caso dos algoritmos de procura e de ordenação, esta operação é a comparação (dados dois elementos, qual deles é o maior?)

Considerações sobre eficiência• A medida de eficiência de um algoritmo de procura ou de ordenação é o número

de comparações que estes efetuam• Dados dois algoritmos, o algoritmo que efetuar o menor número de

comparações é o mais eficiente

Considerações sobre eficiência• Na procura sequencial, percorremos a sequência de elementos até encontrar o

elemento desejado• Se o elemento desejado se encontrar na primeira posição da lista, necessitamos apenas de

uma comparação• Poderemos ter de percorrer toda a lista, se o elemento procurado não se encontrar na lista

• O número médio de comparações cai entre estes dois extremos, pelo que o número médio de comparações na procura sequencial é

𝑛2

em que 𝑛 é o número de elementos na lista

Considerações sobre eficiência• Na procura binária reduzimos para metade o número de elementos a considerar

sempre que efetuamos uma comparação• Se começarmos com 𝑛 elementos, o número de elementos depois de uma passagem é 𝑛/2• O número de elementos depois de duas passagens é 𝑛/4 e assim sucessivamente

• No caso geral, o número de elementos depois de 𝑖 passagens é !"!• O algoritmo termina quando o número de elementos é menor do que 1, ou seja,

terminamos depois de 𝑖 passagens quando!"! < 1

ou𝑙𝑜𝑔"(𝑛) < 𝑖

Considerações sobre eficiência• Para uma lista com 𝑛 elementos, a procura binária não exige mais do que 𝑙𝑜𝑔"(𝑛) passagens

• Em cada passagem efetuamos três comparações• Uma comparação na instrução if e duas comparações para calcular a expressão que

controla o ciclo while

• O número de comparações exigido pela procura binária é inferior ou igual a

3. 𝑙𝑜𝑔"(𝑛)

Considerações sobre eficiência• Na ordenação por borbulhamento e por seleção

12 . (𝑛

" − 𝑛)

Notação do O-maiúsculo• A preocupação com a eficiência está relacionada com problemas que envolvem

um número muito grande de elementos• Se a lista em que efetuamos uma procura tiver 100 000 000 elementos

• A procura sequencial requer uma média de 50 000 000 comparações• A procura binária requer, no máximo, 61 comparações

• Aproximação da quantidade de trabalho necessário em função do número de elementos considerados

• Notação matemática conhecida por notação do O-maiúsculo• Usada para descrever o comportamento de uma função em termos de outra

função mais simples, quando o seu argumento tende para o infinito

Notação do O-maiúsculoSejam f(x) e g(x) duas funções com o mesmo domínio definido sobre o conjunto dos números reais. Escrevemos

𝑓 𝑥 = 𝑂(𝑔 𝑥 )

se e só se existir uma constante positiva 𝑘, tal que para valores suficientemente grandes de x,

𝑓(𝑥) ≤ 𝑘. 𝑔(𝑥)

ou seja, a partir de um valor 𝑥# 𝑘. 𝑔(𝑥) dominar 𝑓(𝑥)

Notação do O-maiúsculoA ordem de magnitude de uma função é igual à ordem do seu termo que cresce mais rapidamenteA ordem de magnitude de 𝑛" + 𝑛 é 𝑛"

O(𝑛" + 𝑛) = 𝑛"

• A procura binária é de ordem 𝑂(𝑙𝑜𝑔! 𝑛 )• A procura sequencial é de ordem 𝑂(𝑛)• Tanto a ordenação por borbulhamento como a ordenação por seleção são de

ordem 𝑂(𝑛")

Duração comparativa