Você está na página 1de 4

Faculdade de Ciências e Tecnologias

Relatório Projeto 2 de AED


Aluno: Vasile Karpa Número: a74872

Aluno: Tiago Alexandre Cortez Antunes Número: a76920

Projeto 2
Neste segundo projeto, o objetivo é aprender sobre a criação e utilização de estruturas de dados
para representar coleções de objetos. Especificamente, iremos trabalhar na implementação de uma
fila com tamanho limitado usando um array e uma lista "Sovina".

QueueArray.java
O problema A envolve a implementação da classe QueueArray, que representa uma fila com
tamanho máximo limitado. Os métodos que iremos testar empiricamente são: dequeue() e
enqueue(), e incluem ensaios de razão dobrada, para determinar o tempo de execução médio e a
ordem de crescimento temporal dos métodos.
No método enqueue() e dequeue() não é possível obter dados válidos para se conseguir criar um
gráfico e comparar. Isto acontece, devido ao tempo dos métodos ser menor que o tempo mínimo
que os testes temporais conseguem alcançar, causando o valor do tempo ser sempre 0 e o tempo do
r estimado tambem ser erradamente 0.
Como r não pode ser 0, considera-se como 1.

StingyList.java
No problema B, uma desvantagem das listas duplamente ligadas em relação às listas simplesmente
ligadas é o uso de mais memória devido à necessidade de armazenar dois ponteiros em cada nó. No
entanto, existe uma implementação alternativa de lista duplamente ligada, chamada "Lista Sovina",

1
que supera essa desvantagem. Na Lista Sovina, nós individuais possuem apenas um único ponteiro,
no qual é armazenado o resultado da operação de “Ou Exclusivo” (XOR) aplicada aos ponteiros
anterior (previous) e seguinte (next).

Pode parecer peculiar, mas aproveita as propriedades do operador XOR. Quando fazemos p⨁n,
obtemos um novo valor em que, para cada bit, 0 representa que p e n são iguais naquele bit e 1
representa que são diferentes. Isso significa que, a partir de p⨁n, podemos obter n se tivermos p, e
podemos obter p se tivermos n.

Podemos demonstrar isso facilmente aplicando novamente o operador XOR a partir de p⨁n:
(p⨁n)⨁n = p⨁(n⨁n) = p⨁0 = p
Da mesma forma, se aplicarmos o operador XOR a partir de p⨁n com p, obtemos:
(p⨁n)⨁p = (n⨁p)⨁p = n⨁(p⨁p) = n⨁0 = n
Em resumo, ao armazenar p⨁n dentro de cada ponteiro p, podemos facilmente obter o endereço de
n se tivermos o de p, e vice-versa. Isso é especialmente útil quando percorremos uma lista
duplamente ligada.
Esta lógica também é válida quando o nó que vamos aceder é o primeiro ou o último nó da lista, mas
nesse caso, os valores de previous e/ou next são NULL, aplicamos o operador XOR com NULL.

Os valores gerados pelo método get() quando testada a sua eficiência indicam que o tempo de
execução vai subindo (depende do tamanho da lista representado pela coluna "size") e a média da
razão dobrada é 1,865420288 ~ 2 1. Isso sugere que o método get() possui uma complexidade
temporal igual a O(n), ou seja, o seu tempo de execução depende do tamanho da lista.

Get(i)
120000000

100000000
f(x) = 3.12971331774509 x + 34502.9686993044
R² = 0.997232600770971
80000000

60000000
Time

40000000

20000000

0
0 10000000 20000000 30000000 40000000
Size

2
A análise dos resultados para o método getSlow() revela uma complexidade temporal parecida, mas
ligeiramente maior. Enquanto o tempo de execução é inicialmente próximo de zero para índices
baixos, ele aumenta à medida que o tamanho da lista aumenta. Isso sugere que o método getSlow()
tem uma complexidade temporal linear, O(n),visto que a media de r = 2,2229301466361844 ~ 2 1.

O método reverse() é responsável por inverter a ordem dos elementos numa lista, é eficiente em
termos de tempo, pois a inversão dos elementos na lista não depende do tamanho da lista.

A operação de inversão será executada em tempo constante, O(1).

Em comparação com os métodos get() e getSlow(), o método reverse() é uma operação muito
diferente, atua diretamente nas referências de início e fim da lista para inverter a ordem dos
elementos. Portanto, a complexidade temporal do método reverse() é bastante simples e não está
relacionada ao número de elementos na lista.

3
Conclusão:
Com base nos dados obtidos e nos resultados dos testes empíricos, podemos realizar uma análise do
tempo de execução médio e da ordem de crescimento temporal dos métodos get(int index) e
getSlow(int index) da classe StingyList.

Após imensos testes para cada função, o gráfico acima mostra a sobreposição dos dois métodos
getSlow() e get(), e comparando os resultados de ambos, fica claro que o método get() é
significativamente mais rápido do que o método getSlow() para aceder elementos numa lista mesmo
ambos possuírem complexidade O(n).

Se os testes em cada iteração fossem perfeitos não teríamos alguns pontos da reta tão dispersos, isto
pode dever-se à sobrecarga do sistema, erros de medição, picos de atividade do CPU devido a outros
processos em segundo plano, a alocação e desalocação de memória durante a execução do
programa podem introduzir variações nos tempos de execução devido à latência de acesso à
memória.

Quanto à utilização de uma lista duplamente ligada em comparação com uma lista simples, podemos
concluir que a primeira é mais rápida que a segunda, mesmo tendo a mesma complexidade temporal
para a procura de um dado elemento nela.

Isto vê-se com a comparação dos métodos get() e getSlow(), que representam a procura de um
índice numa lista duplamente liga e numa lista ligada genérica, respetivamente.

Você também pode gostar