Aula 02¶
Referências úteis :¶
- https://www.ime.usp.br/~pf/analise_de_algoritmos/aulas/amortized.html
Contador Binário¶
public static increment(int[] a) {
int k = a.length;
int i = 0;
while (i < k && a[1] == 1) {
a[i] = 0;
i = i +1;
}
if (i < k) {
a[i] = 1;
}
}
Consumo de tempo (de cada linha do código)¶
| Linha | Consumo de tempo |
|---|---|
| 1 | O(1) |
| 2 | O(k) |
| 3 | O(k) |
| 4 | O(k) |
| 5 | O(1) |
| 6 | O(1) |
Consumo de tempo : O(k)
Sequência de n chamadas¶
incr(), incr(), incr()…. inc() : n chamadas
Consumo de tempo é \(O(nk)\), o que é um exagero (eu não analisei os casos, só supus que toda execução vai ser \(O(n)\)).
a[0] muda n vezes
a[1] muda \( \frac{n}{2}\) vezes
a[2] muda \(\frac{n}{4}\) vezes
.
.
.
a[i] muda \(\frac{n}{2^i}\) vezes
O custo então pode ser calculado por :
$$ Custo = \sum_{i=0}^{k}{\frac{n}{2^i}} = n \cdot\sum_{i=0}^{k}{\frac{1}{2^i}} < n\cdot\sum_{i=0}^{\infty}{\frac{1}{2^i}} = 2n $$
Assim, podemos provar que o algoritmo é \(O(n)\)
Custo amortizado¶
O custo amortizado de uma operação é o custo médio da operação quando considerada uma sequência de operações.
Assim, podemos reescrever a eficiência do algoritmo usando a definição acima : **o consumo de tempo de uma sequÊncia de n incrementos é \(O(n)\). O custo amortizado de increment() é \(O(1)\). **
Tabela Dinâmica (pode ser implementada com fila ou pilha)¶
- Implementação :
public class Stack<Item> implements Iterable<Item< {
private Item[] a;
private int n;
public Stack() { //1
a = (Item[]) new Object[MAX];//2
}
public boolean isEmpty() {
return n == 0;
}
public intSize() {
return n;
}
public void push(Item item) {
if (n == a.length)
resize(2*a.length);
a[n++] = item;
}
public Item pop() {
Item item = a[n-1];
a[n-1] = null; //3
n--;
if (n > 0 && n < a.length/2){
resize(a.length/2);
}
return item;
}
private void resize(int cap) {
Item[]temp = (Item[]) new Object[cap];
for (int i = 0; i < n; n++) {
temp[i] = a[i];
}
}
}
/* GLOSSARIO JAVA
1 : construtor da pilha
2 : O argumento vai ser o tamanho da pilha
3: O java tem um garbage collector, ou seja, não precisa ficar dando free nas coisas. Mas para o java liberar memória ele tem que ter certeza que não tem um ponteiro para aquele endereço de memória, por isso tornamos aquele ponteiro nulo.
*/
Consumo de tempo¶
- Consumo de tempo de push é O(n).
- Sequência de push() :
- S0 -> S1 -> S2 -> … Sn
- Si = estado da pilha após o i-esimo push
- custo do push \(O(m)\) - operação individual
- custo da sequência = \(O(m^2)\) - Exagero!
Análise do push¶
| push() | a.length | custo |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 2 | 1+1 (dobrei para 2) |
| 3 | 4 | 1+2(dobrei para 4) |
| 4 | 4 | 1 |
| 5 | 8 | 1+4 (dobrei para 8) |
| 6 | 8 | 1 |
| 7 | 8 | 1 |
| 8 | 8 | 1 |
| 9 | 16 | 1+8 (dobrei para 16) |
Custo :¶
$$ \sum custos = m + \sum_{i = 0}^{k}2^i = m + 2^k - 1 $$
Mas \(k \approx lg(m)\) então : $$ \sum custos < m+2m = 3m $$
- Assim, o consumo de tempo de uma sequência de m push() é \(O(m)\)
- O consumo de tempo amortizado de push é \(O(k)\) : as operações na quais eu dobro o tamanho da pilha são muito custosas, mas a maioria das operações é constante.