Laboratório de AEDS1 – 2016/1
Trabalho Prático 1
Complexidade de Tempo: Análise e Medições
O trabalho é individual. Trabalhos iguais terão notas também iguais a zero.
Este trabalho contém três exercícios, os dois primeiros valem 5 pontos cada e o terceiro vale 10 pontos.
Instruções para o relatório estão no final deste documento.
2. Usando o código a seguir, medir o tempo de execução do laço com comando vazio, para
diferentes valores de N. Anote os resultados em uma tabela. A seguir, repetir o exercício
incluindo a função rand( ) no laço. Plote os tempos em função do N no mesmo gráfico e
compare. A partir das medições estime a ordem de grandeza do tempo de execução de uma
instrução no computador de teste. Mostre como fez esta estimativa, explique seu raciocínio.
Escreva suas conclusões.
#include <stdio.h>
#include <time.h>
# define N 10000000000LLU
int main(){
unsigned long long int i;
clock_t t1, t2;
t1 = clock ();
for (i=0; i < N ; i++) ;
t2 = clock ();
printf ("tempo = %.3e segundos\n",
((double)t2 (double)t1) / (double)CLOCKS_PER_SEC);
}
3. Dados dois conjuntos de números inteiros S1 e S2, cada um com N elementos, armazenados em
vetores, e um número K, projete e implemente um algoritmo para verificar se há pares de
elementos, um de S1 e o outro de S2, cuja soma é K. Use números aleatórios para gerar os
valores. Apresente uma descrição detalhada do seu algoritmo, usando texto e figuras que
auxiliem a explicação do mesmo. Apresente a análise de complexidade do seu algoritmo. Faça
medições de tempo e do número de operações realizadas por seu algoritmo para comprovar a
sua análise de complexidade. Apresente um relatório com a descrição do algoritmo, a análise
de complexidade, as medições realizadas, suas análises dos resultados e conclusões. Anexe o
código impresso.
RELATÓRIOS DE TRABALHOS PRÁTICOS
Para cada exercício faça um pequeno relatório do experimento, descrevendo a solução, os resultados, a
conclusão e o código em anexo. Os relatórios devem ser entregues grampeados em um único
documento. Não colocar capa. Os relatórios devem conter os seguinte itens:
• sua descrição do experimento: apresente o que foi feito, discuta as limitações e dificuldades
encontradas bem como as decisões tomadas durante a realização do experimento;
• apresentação dos resultados: apresente de forma clara e distinta as tabelas e os gráficos
gerados;
• apresente sua análise dos resultados;
• apresente sua conclusão do experimento;
• ao final da redação do relatório, verifique se tudo o que foi pedido está apresentado.
Eu ouço e esqueço; eu vejo e lembro; eu faço e compreendo.
Confúcio, filósofo chinês (551479 BC)
AEDS 1: TRABALHO PRÁTICO 1
nome: Ana Luiza Sanches data de entrega: 18/03/2016
PARTE I
Objetivos
- Determinar os limites numéricos de cada tipo de variável;
- Identificar o erro que ocorre quando esse limite é ultrapassado;
Solução
O algoritmo desenvolvido utiliza a biblioteca limits.h e exibe as constantes que definem os
valores máximos e mínimos que cada tipo de variável possui. Para testar o que ocorre quando o
valor máximo é ultrapassado, foi criada uma variável var, do tipo int. A ela foi atribuído o valor
máximo suportado pelo tipo int, e em seguida esse valor foi incrementado.
Resultados e Análise
A tabela abaixo informa os limites de cada tipo de variável. Variáveis unsigned(sem sinal)
não podem representar números negativos, logo o limite mínimo é 0. Os limites do tipo long int
equivalem aos limites de long long int no computador testado, assim como os limites do tipo
unsigned long int equivalem aos limites de unsigned long long int. É importante saber que esses
limites podem variar de computador pra computador, ou seja, existe um outro computador em que
os limites do int são equivalentes aos limites do long int.
Tipo de variável Valor mínimo Valor máximo
char -128 127
int -2147483648 2147483647
short int -32768 32767
unsigned int 0 4294967295
long int -9223372036854775808 9223372036854775807
unsigned long int 0 18446744073709551615
long long int -9223372036854775808 9223372036854775807
unsigned long long int 0 18446744073709551615
Tabela 1: Valores máximos e mínimos de cada tipo de variável
Solução
Foi utilizado o algoritmo presente no roteiro como base. O algoritmo base é composto por
um laço for que se repete N vezes. Para automatizar o experimento foi criado um segundo laço for,
que executa o trecho do algoritmo base 5 vezes, cada vez com um número diferente para N. O
algoritmo implementado foi executado duas vezes. A primeira para a medição dos tempos de
execução para um loop vazio, e a segunda, para um loop com chamada de função (rand()).
Resultados e Análise
500
400
300
200
100
0
10⁷ 10⁸ 10⁹ 10¹⁰ 10¹¹
entrada
Gráfico 2: Tempo x Entrada - representação qualitativa (gerado pelo software LibreOffice Calc)
Para estimar a ordem de grandeza do tempo de execução de uma instrução no computador
de teste, basta ter o tempo de execução total do programa e o número de vezes que essa instrução é
executada. O algoritmo de loop vazio é o melhor para se fazer esse calculo pois é possível
determinar todas a instruções realizadas (não se sabe as instruções realizadas na função rand()).
Primeiramente, sabe-se que o algoritmo depende apenas de N para determinar o tempo de
execução. Dessa forma a comparação i<N, que se encontra no loop, pode ser escolhida como a
instrução relevante. Como o loop é executado de 1 até N, então a instrução sempre será executada N
vezes. Assim, divide-se o tempo total de execução pelo número de vezes que ela é executada (N,
que é o tamanho da entrada) : tempo de execução da instrução = tempo total de execução / N.
A tabela mostra diversos valores para o tempo de execução de uma instrução, no entanto, o
importante, é apenas determinar a ordem de grandeza. Observa-se que a ordem de grandeza de
uma instrução, em todos os casos foi 10⁻⁹ s. Essa ordem de grandeza da instrução parece adequada
visto que a frequência do clock do computador utilizado é da ordem de 10⁹ Hz.
Conclusão
A chamada de função aumenta o tempo de execução de um programa, ou seja, representa um
aumento de custo do algoritmo. Entretanto, as funções são estremamente importantes na
legibilidade e organização do código e portanto devem ser utilizadas sempre que necessário.
O tempo de execução de uma instrução pelo computador é tão pequeno(10⁻⁹ s) que não
pode ser percebido por humanos, o faz dele uma máquina eficiente.
PARTE III
Objetivos
- Analisar a complexidade de um algoritmo;
- Determinar os casos de complexidade;
Solução
O algoritmo desenvolvido possui 3 funções: a função principal(main()); uma função que
preenche 2 vetores(de forma aleatória) e uma função que verifica se há dois elementos(cada um de
um vetor) que somados resultem em uma constante K.
A função de preenchimento de dois vetores utiliza a função rand() para gerar números
aleatórios.
A função par (última citada) recebe como parametros 2 vetores s1 e s2, o tamanho dos
vetores N, e a constante K.
Resultados e Análise
Tamanho da entrada Tempo de execução (s) Nº de instruções Foi encontrado?
N
10³ 1.003e-02 1000000 não
10⁴ 4.227e-01 100000000 não
10⁵ 9.288e+00 2496789847 sim
10⁶ 2.098e+00 548270191 sim
Tabela 5: Tempo de execução, nº de instruções para diferentes tamanhos de entrada
Conclusão
APENDICE – códigos
Parte I
main()
{
printf("\nchar: min: %d \t max: %d \t",CHAR_MIN, CHAR_MAX)
;
printf("\nint: min: %d \t max: %d \t",INT_MIN, INT_MAX)
;
printf("\nshort int: min: %d \t max: %d \t",SHRT_MIN, SHRT_MAX)
;
printf("\nunsigned int: min: 0 \t max: %d \t",UINT_MAX)
;
printf("\nlong int: min: %ld \t max: %ld \t",LONG_MIN, LONG_MAX)
;
printf("\nunsigned long int: min: 0 \t max: %ld \t", ULONG_MAX)
;
printf("\nlong long int: max: %lld \t min: %lld \t",LLONG_MIN, LLONG_MAX)
;
printf("\nunsigned long long int: max: 0 \t min: %lld \t", ULLONG_MAX)
;
int var = INT_MAX+1;
printf("\nint maximo %d", INT_MAX)
;
printf("\nint maximo+1 %d\n", var )
;
}
Parte II
# define N 10000000LLU
int main()
{
unsigned long long int i;
unsigned long long int x;
clock_t t1, t2;
for(x = 1; x < 100000; x*=10)
{
t1 = clock ()
;
for (i=0; i < N*x; i++)
;
//rand()
;
t2 = clock ()
;
printf ("%llu : %.3e segundos\n", (N*x)
,
((double)
t2 - (double)
t1)
/ (double)
CLOCKS_PER_SEC)
;
}
}
Parte III
#include <stdio.h>
#include <time.h>
# define N 1000000LLU
int par(int v1[N], int v2[N], int K, unsigned long long int *instrucoes) ;
main()
{
clock_t t1, t2;
unsigned long long int i, j, instrucoes = 0;
int s1[N], s2[N], K = rand()
;
preencher(s1,s2) ;
t1 = clock ()
;
if( par(s1, s2, K, &instrucoes)
)
{
t2 = clock ()
;
printf ("\ntempo = %.3e segundos\n",
((double)
t2 - (double)
t1)
/ (double)
CLOCKS_PER_SEC)
;
}else{
t2 = clock ()
;
printf ("\ntempo = %.3e segundos(nao encontrado)
\n",
((double)
t2 - (double)
t1)
/ (double)
CLOCKS_PER_SEC)
;
}
printf("entrada: %llu instrucoes: %llu\n",N,instrucoes)
;
}