---
tags: Lab_ICC_II
---
<center>
<h3>
Universidade de São Paulo (USP)<br>
Instituto de Ciências Matemáticas e de Computação (ICMC)<br>
</h3>
<h3>
Bacharelado de Ciências de Computação<br>
Disciplina: Laboratório de Introdução à Ciência da Computação II<br>
Professor: Leonardo Pereira<br><br>
Aluno: Guilherme Machado Rios (11222839)
</h3>
<h1>
Análise formal e contagem de operações para algoritmos de buscas
</h1>
</center>
## Introdução
Será feita a contagem de operações/análise formal de três algoritmos envolvendo busca em um vetor de inteiros previamente ordenad. O primeiro é uma simples busca sequencial, o segundo e terceiro uma busca binária, iterativa e recursiva, respectivamente.
## Contagem de operações linha a linha
Tomando:
- ***a***: atribuições e operações aritméticas
- ***c***: comparações
- ***p***: acesso a memória
- ***r***: retorno de função
- ***f***: chamada de função
Função ***recursive_bin_search()***:
```c
int recursive_bin_search(int *vector, int key, int low, int high) {
if (low > high) return NOT_FOUND; // lg(n)*[c] + c + r
int mid = (low + high) / 2; // lg(n)*[3*a]
if (vector[mid] < key) { // lg(n)*[p + c]
return recursive_bin_search(vector, key, mid + 1, high);
}
else if (vector[mid] > key) { // lg(n)*[p + c]
return recursive_bin_search(vector, key, low, mid - 1);
// lg(n)*[r + f + a]
}
return mid + 1;
}
// Analisando uma única execução dessa função considerando 1 execução para
// cada ação entre colchetes, temos, com R(1) sendo o caso base:
// R(n) = [c + 3*a + p + c + p + c + r + f + a] + R(n/2)
// R(n) = 2*[c + 3*a + p + c + p + c + r + f + a] + R(n/4)
// R(n) = 3*[c + 3*a + p + c + p + c + r + f + a] + R(n/8)
// Para k chamadas:
// R(n) = k*[3*c + 4*a + 2*p + r + f] + R(n/(2^k))
// O pior caso acontece quando não achamos o elemento procurado, assim, podemos
// encontrar k analisando o caso base:
// n/2^k = 1 <=> n = 2^k <=> k = lg(n)
// Sendo assim, temos que a equação para uma busca recursiva completa, no pior
// caso, é dada por:
// f(n) = lg(n)*(c + 3*a + p + c + p + c + r + f + a) + [R(1)]
// com R(1) = (c + 3*a + p + c + p + c + r + f + a) + c + r, no pior caso ...
// f(n) = lg(n)*(3*c + 4*a + 2*p + r + f) + (4*c + 4*a + 2*p + 2*r + f)
// Sendo c = a = p = r = f = K, temos:
/****************************
| f(n) = 11K*(lg(n)) + 13K |
****************************/
// recursive_bin_search() -> O(lg(n))
```
Função ***iterative_bin_search()***:
```c
int iterative_bin_search(int *vector, int key, int low, int high) {
int mid = (low + high) / 2; // 3*a
if (vector[mid] == key) return mid + 1; // p + c
while (low <= high) { // lg(n)*[c] + c
mid = (low + high) / 2; // lg(n)*[3*a]
if (vector[mid] < key) low = mid + 1; // lg(n)*[p + c]
else if (vector[mid] > key) high = mid - 1; // lg(n)*[p + c + a]
else return mid + 1;
}
return NOT_FOUND; // [r]
}
// Para saber quantas iterações serão feitas por esse laço basta nos
// basearmos no algoritmo recursivo, dado que ambas se baseam na ideia
// de divisão e conquista, logo temos lg(n) iterações...
// f(n) = lg(n)*(c + 3*a + p + c + p + c + a) + 3*a + p + c + c + r
// f(n) = lg(n)*(3*c + 4*a + 2*p) + 3*a + p + c + c + r
// Sendo c = a = p = r = K, temos:
/**************************
| f(n) = 9K*(lg(n)) + 7K |
**************************/
// iterative_bin_search() -> O(lg(n))
```
Função ***sequential_search()***:
```c
int sequential_search(int *vector, int key, int vector_len) {
for (int i = 0; i < vector_len; i++) { // a+c + n*[c + 2*a]
if (vector[i] == key) return i + 1; // n*[c]
}
return NOT_FOUND; // [r]
}
// f(n) = a + c + n*(c + 2*a + c)
// f(n) = n*(3*c + 2*a) + a + c
// Sendo c = a = K, temos:
/**********************
| f(n) = 5K*(n) + 2K |
**********************/
// sequential_search() -> O(n)
```
## Comparação
Analisando as contagens de operações verificamos exatamente a análise feita no [relatório informal](https://hackmd.io/@JBzC_xdjRMidk80D-obQHQ/r19_QG2SD). Vemos as buscas binárias se mostrando melhores no pior caso, com um leve sobressair do algoritmo iterativo, dado que a contagem para esse mostrou uma constante menor.