Em todos os casos a escala do gráfico para os algoritmos

Propaganda
Relatório - Projeto e Análise de Algoritmos
Alunos:

Felipe Rodopoulos de Oliveira – 10/010060

Heitor Henrique de Paula – 10/0104011

Wallace Bruno – 10/0127380
1. Compilação
O trabalho consiste em 5 programas:
1. gera_inteiros.c – programa gerador de sequências de números contidos em um dado
intervalo em um dado tipo de ordenação, colocada em um dado arquivo.
2. quicksort-a1.c – recebe um arquivo com uma sequência de números como entrada,
ordena-os utilizando o método do quicksort com o pivô sendo o primeiro elemento e escreve em
um arquivo de saída com a sequencia de números ordenada.
3. Quicksort-a2.c – recebe um arquivo com uma sequência de números como entrada,
ordena-os em um vetor pelo método do Quicksort randomizado com o pivô sendo um elemento
aleatório do vetor e escreve em um arquivo de saída a sequência de números ordenada.
4. Quicksort-a3.c - recebe um arquivo com uma sequência de números como entrada,
ordena-os em uma lista utilizando o método do Quicksort Randomizado com o pivô sendo um
elemento aleatório da mesma e escreve em um arquivo de saída a sequência de números
ordenada.
5. radixsort.c – recebe um arquivo com uma sequência de números como entrada, ordenaos em um vetor pelo método do Radixsort e escreve um arquivo de saída com a sequência de
números ordenada.
Todos os códigos podem ser compilados pela seguinte diretiva:
gcc –ansi –Wall ARQUIVO_DESEJADO.c –o NOME_DO_EXECUTAVEL
Para rodar o gerador de inteiros e os algoritmos de ordenação, respectivamente:
./gera M N ordenação arquivo-saida.txt
./EXECUTAVEL entrada.txt saída.txt
Cada algoritmo de ordenação foi testado para uma diferente massa de dados N e uma diferente
forma de ordenação da entrada dada. Aqui os dados serão divididos pelos três diferentes tipos de
ordenação: ordenado, invertido e aleatório. As tabelas contém as médias de amostras de 10 execuções
para cada massa de dados e tipo de ordenação.
2. Tabelas e Gráficos
Aqui serão expostos as tabelas com os tempos de execução para cada algoritmo construído para uma
determinada massa de dados N. Seguido, terão os gráficos com os pontos gerados a partir dos dados das
tabelas, sendo que os gráficos conterão 4 linhas, uma para cada algoritmo. Importante notar que os
gráficos terão a escala de log x log para que os pontos exponenciais correspondam a pontos sequenciais.
Em todos os casos a escala do gráfico para os algoritmos de Quicksort Randomizado em lista ficou
muito maior que a dos demais gráficos, necessitando que fossem postos em um gráfico a parte para
melhor visualização.
Importante também notar que em todos os casos, este mesmo algoritmo teve tempos que
ultrapassavam 1h. O algoritmo continuou ser rodado mesmo assim, mas só até certo ponto. Para a
entrada que isso ocorria, as demais entradas tinham os tempos considerados iguais.
2.1. Sequência Ordenada
Quicksort
1–8
1 – 16
1 – 32
1 – 64
1 – 128
1 – 256
1 – 512
1 – 1024
1 – 2048
1 – 4096
1 – 8192
1 – 16384
1 – 32786
1 – 65536
1 – 131072
1 – 262144
1 – 524288
1 – 1048576
1 - 2097152
0.000009
0.000012
0.000011
0.000016
0.000033
0.000074
0.0000249
0.0002031
0.0004487
0.017730
0.062040
0.212823
0.839491
3.345426
13.187542
56.270340
93.505768
142.397339
226.522476
Quicksort
Randomizado
0.000015
0.000021
0.000040
0.000063
0.000113
0.000287
0.000539
0.001581
0.003171
0.005879
0.012074
0.020808
0.043233
0.077316
0.147334
0.292949
0.626966
1.224888
2.356692
Radixsort
0.000015
0.000011
0.000015
0.000022
0.000028
0.000048
0.000095
0.000213
0.000494
0.001050
0.002068
0.005270
0.010492
0.021470
0.040495
0.078008
0.144312
0.308577
0.689914
Quicksort Randomizado
em Lista
0.000034
0.000040
0.000089
0.000131
0.000394
0.001131
0.008122
0.034302
0.118908
0.480016
1.911062
8.840253
38.936981
177.950226
794.067627
3272.633545
4280.775391
4323.832520
4323.832520
2.2. Sequência Aleatória
Quicksort
1–8
1 – 16
1 – 32
1 – 64
1 – 128
1 – 256
1 – 512
1 – 1024
1 – 2048
1 – 4096
1 – 8192
1 – 16384
1 – 32786
1 – 65536
1 – 131072
1 – 262144
1 – 524288
1 – 1048576
1 - 2097152
0.000008
0.000014
0.000012
0.000019
0.000030
0.000052
0.000154
0.000212
0.000588
0.001530
0.001970
0.007818
0.010767
0.023698
0.047460
0.089378
0.176611
0.379993
0.798123
Quicksort
Randomizado
0.000022
0.000029
0.000042
0.000078
0.000155
0.000291
0.000545
0.002499
0.002490
0.006690
0.012685
0.025360
0.050920
0.093955
0.175846
0.351205
0.718770
1.425702
2.882137
Radixsort
0.000013
0.000017
0.000016
0.000020
0.000036
0.000067
0.000109
0.000217
0.000442
0.001112
0.002244
0.005505
0.008815
0.017786
0.037856
0.072612
0.138738
0.300189
0.614032
Quicksort Randomizado
em Lista
0.000019
0.000046
0.000079
0.000122
0.000339
0.001383
0.008226
0.039823
0.140725
0.572997
2.494688
11.248690
43.130264
185.722610
821.222717
3486.802490
4304.854492
4304.854492
4304.854492
2.3. Sequência Invertida
Quicksort
1–8
1 – 16
1 – 32
1 – 64
1 – 128
1 – 256
1 – 512
1 – 1024
1 – 2048
1 – 4096
1 – 8192
1 – 16384
1 – 32786
1 – 65536
1 – 131072
1 – 262144
1 – 524288
1 – 1048576
1 - 2097152
0.000009
0.000012
0.000014
0.000028
0.000078
0.000154
0.001005
0.004154
0.017626
0.060209
0.214840
0.842484
3.434234
13.693277
56.924576
92.959946
122.673454
227.895340
303.842041
Quicksort
Randomizado
0.000021
0.000036
0.000050
0.000084
0.000143
0.000306
0.000656
0.001909
0.002935
0.007731
0.014771
0.026522
0.052145
0.106936
0.192669
0.373644
0.720024
1.453796
2.890021
Radixsort
0.000009
0.000010
0.000013
0.000019
0.000027
0.000043
0.000147
0.000175
0.000813
0.001017
0.002336
0.006497
0.008195
0.018423
0.036267
0.071743
0.147676
0.330471
0.653514
Quicksort Randomizado
em Lista
0.000034
0.000002
0.000067
0.000158
0.000439
0.001413
0.007360
0.031599
0.138473
0.589626
2.507957
9.939127
44.239773
197.220871
797.749695
3296.634521
4210.873535
4304.855469
4304.855469
3. Questões da Especificação
Questão 1
Aqui percebeu-se a seguinte configuração: nos casos onde o vetor estava ordenado ou invertido, o
melhor algoritmo foi o quicksort randomizado em vetor. Para o vetor aleatório, o melhor foi o quicksort
não-randomizado.
Analisando os dados e conhecendo o funcionamento do quicksort pode-se explicar o ocorrido
pelo seguinte: o quicksort não-randomizado irá sempre pegar a primeira posição dos seus subvetores e
usar como pivô. Ou seja, pegando a primeira posição, o algoritmo estará varrendo o vetor analisando que,
no caso do vetor ordenado, todos o elemento serão maiores que o pivô, logo, nada é alterado e teremos
que pegar o elemento seguinte do pivô e rodar o vetor novamente. No caso do vetor desordenado, todos
os elementos serão menores que o pivô, logo, este será trocado de posição com todos e o elemento
seguinte será analisado. A relação de recorrência que estamos caindo aqui é:
O que configura uma alta complexidade. No quicksort randomizado, o pivô é escolhido
aleatoriamente, logo a cada partição do vetor, estaremos em um novo lugar. Este lugar pivô pode ser um
lugar de pior, médio ou melhor caso, mas nunca será sempre o de pior. Logo a relação de recorrência que
ocorre para o randomizado é:
Caindo numa recorrência média e bem melhor do que a do quicksort não-randomizado.
Para explicar o caso do quicksort ser mais eficiente no randomizado, basta averiguar a distribuição de
elementos no vetor a qual está em ordem aleatória. Logo, o fator de aleatoriedade já está sendo aplicado
ao vetor e por fim, o quicksort irá ter um comportamento de um quicksort, porém, mais comportado,
uma vez que está pegando sempre o primeiro elemento do subvetor. Desta forma, mostra-se mais
eficiente
Questão 2
Em todos os casos, a versão de manipulação em vetores foi bem mais rápida. Analisando as
tabelas percebe-se uma grande discrepância entre os tempos de execução, o que mostra que forma de
armazenar os inteiros com certeza influi na complexidade do algoritmo. Em listas, para realizar trocas e
achar elementos era necessário percorrer a lista, uma tarefa bem custosa para algoritmos que
necessitam de uma grande liberdade para percorrer memória.
Questão 3
Conhecendo-se o tamanho da palavra (b) e definindo o r, para entradas de dados n, temos a seguinte
complexidade para o radix:
Esta relação mostra que a quantidade de passos necessárias para a execução do Radixsort depende de r
e o trabalho feito em cada passo também depende de r. Visto isso, fez-se o pedido na especificação e
obteve-se os seguintes dados que permitiram a construção do Gráfico 4. Obs: como não foi especificado,
foi feito apenas para o caso de entradas aleatórias.
R
131072
262144
524288
1048576
2097152
1
0.04592300
0.07730900
0.14849500
0.34529001
0.78763103
2
0.03746800
0.08226800
0.16816001
0.32857099
0.74224997
4
0.03937000
0.08151603
0.16309901
0.35172600
0.72794300
8
0.04685300
0.08681601
0.17136200
0.33312398
0.68809998
16
0.04580599
0.07052100
0.15192100
0.30043599
0.62585700
32
0.04253500
0.07697600
0.15448201
0.30538303
0.86256099
Analisando o obtido, foi possível perceber que o R como 16 foi o mais eficiente seguido do 8, 4, 2, 1 e
finalmente o R = 32 como o menos eficiente. A explicação para tal é simples:
Observando a relação de recorrência do Radix, observamos que cuidado que devemos ter para
que a constante 2r não ultrapasse o valor de N, pois caso isso ocorra, N não será mais a variável
determinante da relação de recorrência e sim a constante dependente de R.
Logo, quando R é 32 e nenhuma das massas de teste é maior que 232, então a relação de
recorrência cresce em função desta constante. Desta forma, tem-se o pior caso. Para evitar tal, é
necessário garantir que R ≤ lg(n).
O melhor caso, que ocorre para R = 16, vem-se do caso limite da relação anterior representada.
Como nossa massa de teste vai até o caso de 2097152 = 221 e o maior R que não ultrapassa 21 é o R = 16,
temos o caso onde há a maior divisão da palavra de 32 bits por R nos nossos casos de R. Assim, verificase que quando R = 16, o algoritmo executa sobre 2 passos e com o trabalho de (n + 216) em cada um deles,
sendo que esta potência de 2 é uma constante que será ignorada por não chegar ao valor de N, fazendo
com que tenhamos o melhor caso por ter menos passos dentre de todos os R’s testados. Logo, os
resultados obtidos estão dentro do esperado.
Questão 4
Analisando o obtido de todos os resultados, foi constatado que o Radixsort foi o mais rápido em
todos os casos. Este resultado é compreensível, de modo que o Radixsort é um algoritmo que trabalha
livre de comparações e com base em outro algoritmo, o Counting Sort, que tem uma eficiência de θ(n +
k). Nos casos testados aqui, tem-se que n = k, então, a complexidade se reduzia a θ(n + n) = θ(2n) = θ(n).
Vemos que o Radix trabalhava sob passos, sendo que estes tinham complexidade θ(n + 2r), pois
executavam usando o Couting Sort como base. Como 2r foi tratado como constante, então os passos
rodavam com complexidade θ(n) e tendo D passos, temos a complexidade de θ(D.n).
Comparativamente, o quicksort pode ter no mínimo uma complexidade de θ(n . log(n)), por ser
um algoritmo de comparação e por essa razão, acabou por perder em todos os casos. Assim, os resultados
aqui no experimento seguiram o esperado.
Download