Documente Academic
Documente Profesional
Documente Cultură
Abstract. This report aims to present the main algorithms for finding the shortest path between all pairs of nodes in a graph and these from all and all for
everyone. The algorithms will be presented Dijkstra, Bellman-Ford, FloydWarshall with detection negative cost cycles, as well as their complexity, characteristics, implementation, results of experiments and final considerations.
Resumo. Este relatorio tem como principal objetivo apresentar os principais algoritmos para encontrar o caminho de custo mnimo entre todos pares de nos de um grafo sendo estes de todos e de todos para todos. Serao
apresentados os algoritmos de Dijkstra, Bellman-Ford, Floyd-Warshall com
detecca o de ciclos de custo negativo, assim como suas complexidades, caractersticas, implementaca o, os resultados obtidos dos experimentos realizados e
consideraco es finais.
Tais arcos tambem podem ser ponderados sendo sua funca o peso w(u, v) : E R
[Netto 2003].
w(p) =
k
X
w(vi1 , vi )
i=1
1.1.1. Prova da Subestrutura Otima
de um Caminho Mais Curto
Cormen [Cormen 2002] continua explicando claramente como e o lema e a prova deste
problema:
Lema: Dado um grafo orientado ponderado G = (V, E) com funca o peso w :
E R, seja p = hv1 , v2 , . . . , vk i um caminho mais curto do vertice v1 ate o vertice vk e,
para quaisquer i e j tais que 1 i j k, seja pij = hvi , vi+1 , . . . , vj i o subcaminho p
desde o vertice vi ate o vertice vj . Entao, pij e um caminho mais curto de vi e vj .
p1i
pij
pjk
p0ij
pjk
Os vertices h, i e j sao bem definidos pois todos sao inacessveis, tornando seu valor +, ou seja, um
valor bem definido segundo a definica o do problema.
0
wij = o peso da aresta orientada (i, j)
se i = j,
se i 6= j e (i, j) E,
se i 6= j e (i, j)
/ E.
1.4. Aplicaco es
O problema de caminhos mnimos se adapta a diversas situaco es praticas. Por exemplo,
em roteamento pode-se modelar os vertices de um grafo como os cruzamentos, as arestas
como vias, e os custo como distancia percorrida. Outra aplicaca o comumente utilizada
e em redes de computadores, onde os nos representam o equipamentos, os arcos os trechos de cabeamento e os custos as taxas de transmissao, onde a soluca o seria a rota de
transmissao mais rapida.
Estes problemas de uma u nica origem envolvem naturalmente problemas relacionados com sequencias de decisoes (escolhas de itinerarios ao longo de uma viagem ou
tracado de uma estrategia em um problema de investimentos). Sendo assim, trata-se de
decisoes envolvendo alguma forma de custo a ser minimizado. O problema de todos para
todos seria u til numa possvel elaboraca o de uma tabela de distancia entre todos os pares
de cidades de um certa regiao para um atlas rodoviario.
2. Os Algoritmos
Nesta seca o, sera descrito como a literatura aborda os algoritmos, suas principais caractersticas e a sua implementaca o junto com a analise de sua complexidade assintotica.
2.1. Consideraco es de Projeto e Analise
Para que o usuario fique ciente de algumas decisoes utilizadas na implementaca o, esta
seca o tratara de alguns detalhes que sao importantes para compreender o projeto dos
algoritmos:
Alguns algoritmos implementados tratam o infinito como: o maior peso encontradas das arestas multiplicado por ele mesmo.
Isso pois, ele sera o maior valor e, comparado a todos os outros valores, ele e
considerado infinito.
Nenhum algoritmo faz teste de verificaca o de entradas invalidas.
E necessario que todas as instancias sejam compatveis para cada um dos algoritmo, ou seja, nao deve-se colocar uma instancia com peso negativo no Algoritmo
do Dijkstra pois ele nao avisara erro e tentara executar normalmente.
Foi executado em todos os algoritmos analisadores de codigo estaticos e
dinamicos. Executou-se primeiramente o Clang Static Analyzer e em seguida o
Valgrind. Com exceca o do Algoritmo de Bellman Ford, todos retornaram sucesso
nas analises.
O Algoritmo de Belmman Ford encontrou-se o seguinte warning que nao fora
resolvido, devido a nao diminuir o desempenho de algoritmo:
scan-build: Using /Users/pripyat/clang/bin/clang
for static analysis
main.c:147:18: warning: Potential memory leak
* maior_peso = -1;
1 warning generated.
scan-build: 1 bug found.
Item
Processador
Nucleos
Cache L2 (por Nucleo)
Cache L3
Memoria RAM
Arquitetura
Sistema Operacional
Versao do Kernel
Compilador
Descrica o
1 Processador Intel Core i7 - 2,9 GHz
4 Nucleos
256 KB
4 MB
10 GB DDR3
Arquitetura de von Neumann
OS X 10.11.4 (15E65)
Darwin 15.4.0
Apple LLVM version 7.3.0 (clang-703.0.31)
2.3. Relaxamento
Segundo Cormen [Cormen 2002] os algoritmos possuem a tecnica de relaxamento onde
para cada vertice v V , mantem-se um atributo d[v], chamado de estimativa de caminho
mais curto, que e o limite superior sobre o peso do caminho mais curto entre s e v.
Assim, como primeiro passo, a estima de distancia de cada vertice e infinito, ja que o
algoritmo nao sabe de antemao qual e o caminho mais curto ate cada um deles2 . Faz-se
uma estimativa pessimista inicial para o caminho mnimo ate cada vertice: d(v) = .
O processo de relaxar uma aresta (u, v) consiste em testar alguma forma de melhorar o caminho mais curto para v encontrado ate agora por outros caminhos intermediarios
que utilizem u. Um relaxamento realiza a alteraca o dos valores conhecidos das distancias
atuais atualizando-os para novos valores de acordo com os caminhos intermediarios testados atualizando o novo predecessor de v como e exibido na Figura 4.
Na Figura 4, existe um caminho ja conhecido que utiliza a aresta (u, v) com custo
de 9 unidades. O relaxamento consiste em encontrar outro caminho que utiliza um custo
menor que o atual que no caso foi encontrado uma aresta com peso 2 partindo de d[u] = 5
totalizando d[v] = 7, ou seja, um caminho intermediario menor que o atual. O novo valor
e atualizado e o processamento do algoritmo continua ate que todos sejam analisados.
2
Algorithm 2 Dijkstra
1: procedure D IJKSTRA(G, w, s)
2:
INICIALIZA-UNICA-FONTE(G, s);
3:
S ;
4:
Q V [G];
5:
while Q 6= do
6:
u RETIRA-MINIMO(Q);
7:
S S {u};
8:
for cada vertice v Adj[u] do
9:
RELAXA(u, v, w);
10:
end for
11:
end while
12: end procedure
Este algoritmo nao possui detector de ciclo negativos. Alias, como ja mencionado,
este algoritmo nao suporta arestas negativas.
Algorithm 3 Floyd-Warshall
1: procedure F LOYD -WARSHALL(W )
2:
n linhas[W ];
3:
D W;
4:
for k 1, n do
5:
for i 1, n do
6:
for j 1, n do
k1
k1
7:
dkij min(dk1
ij , dik + dkj );
8:
end for
9:
end for
10:
end for
11:
return D;
12: end procedure
Alem da matriz de pesos, tambem e necessario uma matriz de mesma dimensao
que armazenara os predecessores de cada vertice que no pseudocodigo foi nomeada de
D. Inicialmente, ela e preenchida com os valores das arestas obtidas dos dados de entrada
e serao modificadas a cada relaxamento realizado ao longo da execuca o do algoritmo
[Cormen 2002].
Este algoritmo assume que sua entrada nao inclui nenhum ciclo negativo.
2.7. Analise do Algoritmo
Cada laco for deste algoritmo pode ser entendido como um somatorio de um mesmo
valor constante supondo que a operaca o da linha 7 e uma operaca o elementar. Assim,
temos que sua complexidade e dada por
n X
n X
n
X
Ja que cada somatorio e a repetica o de o valor 1 n vezes, entao este pode ser
convertido para
n X
n
X
k=1 i=1
Portanto
n3
Entao, sua complexidade assintotica pode ser descrita como O(n3 ).
3. Experimentos
Para a realizaca o dos experimentos, utilizou-se de um script em Shell para a execuca o
dos testes de forma controlada e autonoma. Para cada iteraca o, foi analisado o tempo de
execuca o e assim comparado com os demais algoritmos.
Para tal teste, utilizou-se de 3 (tres) instancias, entretanto, para que a execuca o
de grafos com peso negativo sejam testados tambem, foi selecionado uma das instancias
e suas arestas foram alteradas para que se comporte como um grafo com arestas negativas, totalizando 4 instancias. Sao elas: rome99.gr, rg300 4730.gr, rg300 768 floyd.gr,
rg300 768 floyd-n.gr sendo a u ltima modificada.
Isso foi necessario pois as instancias com arestas de peso negativo encontradas na
literatura eram grandes e colocaria o projeto em risco ja que executar 20 (vinte) iteraco es
de um problema poderia levar tempo demais para logo analisar, gerar graficos e tabelas e
a conclusao deste.
Para cada instancia foi executada 20 (vinte) vezes no mesmo ambiente de testes
para que os dados tenham uma media de tempo confiante.
Todos os resultados de cada iteraca o sao exibido em anexo neste relatorio, junto
com cada algoritmo implementado.
3.1. Formato de Sada
Como meio de tornar o algoritmo u til, decidiu-se que ele teria como sada de arquivo a
impressao de todos os caminhos por ele analisados (que no caso e todos para todos). O
formato deste esta da seguinte forma:
[origem,destino](distancia) caminho_origem_ate_destino
Um exemplo e exibido a seguir:
[1,2](8) 1 4 2
[1,3](9) 1 4 2 3
[1,4](5) 1 4
3.2. Analise de Tempo do Problema de Caminho Mnimo de Todos para Todos
Para melhor visualizaca o dos resultados, foram desenvolvidos tabelas e graficos.
Abaixo e exibido a tabela com os valores medios de cada algoritmo sobre cada
instancia. As tabelas com os valores de cada iteraca o e exibido em anexo no relatorio.
Deve-se notar que a instancia rg300 768 floyd-n.gr e a instancia que foi alterada
pelo grupo para que se comporte como um grafo com arestas de peso negativo e por isso,
foi adicionado um -n no seu nome para diferenciar das outras.
Como ja dito anteriormente, o Algoritmo de Dijkstra nao suporta grafos com arestas negativas e por isso, seus dados sobre esta instancia nao se aplicam.
instancias
com o objetivo de obter caminhos mnimos de todos para todos.
Instancia
rome99.gr
rg300 4730.gr
rg300 768 floyd.gr
rg300 768 floyd-n.gr
Bellman-Ford (s)
420.0757
1.788467
0.294253
0.289948
Dijkstra (s)
93.59347
0.11464
0.094997
Nao se aplica.
Ford-Warshall (s)
149.3292
0.118966
0.116002
0.098479
Figura 7.
Tempo de execucao
de cada algoritmo sobre a instancia
rg300 768 floyd.gr. Fonte: Autor.
3.3. Conclusao
Primeiramente, algo interessante a se notar e que o problema de caminhos mnimos
tambem esta relacionado a programaca o linear. E possvel reduzir um caso especial de
programaca o linear ao fato de encontrar caminhos mais curtos a partir de uma u nica origem. Tal problema pode ser resolvido com algoritmo de Bellman-Ford, sendo assim
resolvendo tambem o problema de programaca o linear [Cormen 2002].
Os resultados foram expressos por meio de tabelas e graficos (Boxplot) pelo fato
de ambos serem itens fundamentais para a compreensao do leitor do relatorio. Realizou-se
20 iteraco es de teste devido ao tempo computacional elevado pelo tamanho das instancias
utilizadas assim como a complexidade dos algoritmos.
Em termos de implementaca o, todos os codigos possuem grande facilidade de
implementaca o. Principalmente o Algoritmo Floyd Warshall que se baseia numa simples
estrategia que utiliza tres lacos for aninhados e realiza uma u nica verificaca o e atribuica o
dentro deles. Entretanto, em analise assintotica sua facilidade se torna um problema.
Na analise assintotica houve uma grande disputa entre o Bellman-Ford e Dijkstra.
O Algoritmo de Floyd Warshall ficou fora dessa disputa por ser de complexidade de tempo
O(n3 ) e o Bellman tem complexidade O(n2 ) e o Dijkstra O(E + V log V ) no pior caso.
O algoritmo Dijkstra teve sucesso sua complexidade de tempo ser menor que todos os
outros. A estrutura utilizada nele foi projetada pelos integrantes do grupo. Tambem houve
varios problemas com Warnings que o compilador do Bellman-Ford relatou que, como ja
mencionado, o grupo nao soube explicar a origem.
4. Algoritmos
4.1. Shell Script para Execuca o Autonoma
1
#!/bin/bash
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Compila
eval "gcc
eval "gcc
eval "gcc
os arquivos novamente
bellman/bellman.c -o bellman/bellman"
floyd/floyd.c -o floyd/floyd"
dijkstra/dijkstra.c -o dijkstra/dijkstra"
16
17
18
# Vetor de instancias
instancias=( rome99.gr rg300_4730.gr rg300_768_floyd.gr rg300_768_floyd-n.gr )
19
20
21
22
23
24
25
26
# Execucoes
for algoritmo in "${algoritmos[@]}"
do
echo $algoritmo
27
28
29
30
31
32
33
34
35
36
37
38
39
40
cmd="./$algoritmo/$algoritmo $instancia"
date
echo $cmd
$cmd
done
echo
41
42
43
done
echo
44
45
done
/*
alise de Algoritmo
* Trabalho de Projeto e An
o - Turma 16.1
encia da Computac
a
* Mestrado em Ci
*
* Alunos (nome, matricula, e-mail):
Conrado
*
Danilo Santos Souza
16.1.10149 - danilo.gdc@gmail.com
*
Rodolfo Labiapari Mansur Guimar
aes 16.1.10163 - rodolfolabiapari@gmail.com
*
Thiago Schons
16.1.10186 - thiagoschons2@gmail.com
*
*
* Este arquivo executa o Algoritmo de Bellman Ford.
*
*
* Para executar o arquivo utilize o comando:
"./nomeDoPrograma benchmark"
*
*
*
da est
a descrita da seguinte forma: [origem, destino](distancia) caminho
* Sa
Abaixo
e
exibido um exemplo
*
* [1,2](8) 1 4 2
* [1,3](9) 1 4 2 3
* [1,4](5) 1 4
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
28
29
30
31
32
33
34
35
36
/*
* Estrutura que armazena os valores de uma aresta
*/
typedef struct Edges {
int origem;
int destino;
float peso;
} Edge;
37
38
39
40
41
42
43
44
45
46
47
/*
da na forma
* Utilizou-se de uma pilha para imprimir a ordem de sa
* origem -> destino. O algoritmo naturalmente imprime de forma inversa, e por
* isso necessitou de uma pilha.
*/
typedef struct Pilhas {
int noh;
struct Pilhas * prox;
} Pilha;
48
49
50
51
52
53
54
/*
* Procedimento de empilhar um novo item na pilha
*/
void pushPilha (Pilha ** s, int noh) {
Pilha * noh_pilha = calloc(1, sizeof(Pilha));
55
56
noh_pilha->noh = noh;
noh_pilha->prox = *s;
57
58
*s = noh_pilha;
59
60
61
62
63
64
65
66
67
/*
* Procedimento de retirar um item da pilha
*/
int popPilha (Pilha ** s) {
int retorno;
Pilha * proximo;
68
if (s != NULL) {
retorno = (*s)->noh;
proximo = (*s)->prox;
69
70
71
72
free(*s);
73
74
*s = proximo;
75
76
return retorno;
77
78
} else {
return -1;
}
79
80
81
82
83
84
85
86
87
88
/*
* Procedimento que realiza a retirada dos dados da pilha imprimindo cada um
* deles.
*/
void imprimePilha(Pilha * s, FILE * f) {
89
if (s == NULL) {
return;
} else {
90
91
92
93
while (s != NULL) {
fprintf(f, " %d", s->noh);
popPilha(&s);
}
94
95
96
97
}
fprintf(f, "\n");
98
99
100
101
102
103
104
105
106
/*
* Procedimento que retira todos os itens da pilha
*/
void esvaziaPilha(Pilha * s) {
107
108
109
110
111
112
113
114
115
if (s == NULL) {
return;
} else {
atual = s;
proximo = s->prox;
116
117
118
atual = proximo;
proximo = atual->prox;
119
120
121
122
free (atual);
123
124
125
126
127
128
129
130
131
/*
es do
avel por ler o arquivo e recolher as informac
o
* Procedimento respons
grafo
nele
contido.
*
*/
void le_arquivo(char * diretorio, Edge ** lista_arestas, int * vertices, int * arestas,
,
int * maior_peso) {
132
133
134
135
136
137
138
139
140
141
142
143
144
// L
e do arquivo o comando da linha
char comando = fgetc(bench);
145
146
147
// Enquando n
ao for final de arquivo
while (comando != EOF) {
148
149
150
151
152
153
o comando
// Verifica qual comando e
switch (comando) {
// Coment
arios ser
ao ignorados
case c:
while(fgetc(bench) != \n) ;
154
155
break;
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// Le o n
umero de vertices e arestas
174
175
176
177
178
// Vari
avel indicando o maior peso encontrado no momento
maior_peso
= -1;
*
179
180
181
182
183
184
185
break;
186
187
case a:
188
189
190
191
192
193
194
195
// L
e a aresta
fscanf(bench, "%d %d %d", &origem_tmp, &destino_tmp, &peso_tmp);
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
// Quebra a linha
fgetc(bench);
211
212
213
break;
214
215
default:
break;
216
217
218
219
220
221
222
// Le o proximo comando
comando = fgetc(bench);
} //while
223
224
225
226
227
228
ao ao arquivo.\n");
exit(-1);
}
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
/*
ao de das dist
ancias encontradas
* Procedimento simples para impress
/
*
void imprimeDistancias(int * distancia, int vertices) {
int i;
printf("\n\n\n");
for (i = 0; i < vertices; i++) {
printf("%4d ", i);
}
247
printf("\n");
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
/*
ertice
* Procedimento que imprime os predecessores de cada v
/
*
void imprimePredecessores(int * predecessor, int vertices) {
int i;
printf("\n\n");
for (i = 0; i < vertices; i++) {
printf("%4d ", i);
}
265
printf("\n");
266
267
268
269
270
271
272
273
274
275
276
277
278
279
/*
* Algoritmo De Belmman-Ford.
odigo do livro do Cormen.
* Baseado no pseudoc
/
*
void bellmanFord(Edge * lista_arestas, int ** distancia, int ** predecessor, int
,
vertices, int arestas, int origem, int maior_peso) {
int i, j, peso;
280
281
282
283
284
285
286
287
288
289
290
291
292
0
// Informa que a dist
ancia da origem pra ela mesma e
distancia_temp[origem - 1] = 0;
293
294
295
// Executa o algoritmo
for (i = 0; i < vertices; i++) {
for (j = 0; j < arestas; j++) {
peso = lista_arestas[j].peso;
296
297
298
299
300
301
302
303
304
305
predecessor_temp[lista_arestas[j].destino - 1] = lista_arestas[j].origem ,
1;
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
/*
o do usu
a
ario
* Procedimento final que imprime o caminho para melhor visualizac
bem
como
o
valor
total
da
dist
a
ncia.
*
*/
void imprimeTodosCaminhos(FILE * file, int * distancia, int * predecessor, int vertices,
,
int origem) {
333
334
int anterior, i;
335
336
Pilha * stack;
337
338
339
340
341
342
343
stack = NULL;
344
345
pushPilha(&stack, i + 1);
346
347
anterior = predecessor[i];
while(anterior != origem - 1) {
pushPilha(&stack, anterior + 1);
348
349
350
351
anterior = predecessor[anterior];
352
353
354
355
356
imprimePilha(stack, file);
357
358
359
360
361
362
363
364
365
366
/*
orias alocadas pelo algoritmo
* Desalocas as mem
*/
void desaloca(Edge ** lista_arestas, int ** distancia, int ** predecessor) {
367
free(*lista_arestas);
free(*distancia);
free(*predecessor);
368
369
370
371
372
373
374
375
376
377
378
379
if(argc == 2) {
// Vari
aveis de c
alculo de tempo
clock_t tempo_inicio, tempo_final;
double intervalo_real = 0;
380
381
382
383
384
385
386
387
388
389
390
391
tempo_inicio = clock();
bellmanFord(lista_arestas, &distancia, &predecessor, vertices, arestas, i +
,
1, maior_peso);
tempo_final = clock();
392
393
394
395
396
397
398
399
400
401
402
fclose(file);
fclose(tempos);
403
404
405
406
407
}
else {
printf("Argumentos Inv
alidos!\n");
exit(-1);
}
408
409
410
411
412
413
return (EXIT_SUCCESS);
414
415
/*
alise de Algoritmo
* Trabalho de Projeto e An
o - Turma 16.1
encia da Computac
a
* Mestrado em Ci
*
* Alunos (nome, matricula, e-mail):
Conrado
*
Danilo Santos Souza
16.1.10149 - danilo.gdc@gmail.com
*
Rodolfo Labiapari Mansur Guimar
aes 16.1.10163 - rodolfolabiapari@gmail.com
*
Thiago Schons
16.1.10186 - thiagoschons2@gmail.com
*
*
* Este arquivo executa o Algoritmo de Dijkstra.
o a
` estrutura de dados utilizada j
a
a que os autores
* Deve-se ter atenc
o deste.
decidiram
utilizar
um
estrutura
modificada
para
facilitar
a execuc
a
*
*
*
* Para executar o arquivo utilize o comando:
"./nomeDoPrograma benchmark"
*
*
*
da est
a descrita da seguinte forma: [origem, destino](distancia) caminho
* Sa
exibido um exemplo
* Abaixo e
* [1,2](8) 1 4 2
* [1,3](9) 1 4 2 3
* [1,4](5) 1 4
*/
26
27
28
29
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
30
31
32
33
34
35
36
37
38
39
40
/*
es do n
o
o adjacente, possuindo
* Estrutura que guarda informac
oximo n
o adjacente
* um ponteiro para o pr
* Esta estrutura depente da estrutura NohsIndividuais.
*/
typedef struct Nohs {
o do Noh
int noh_id;
// Identificac
a
int peso;
// Peso que esta adjac
encia tem
struct Nohs * prox; // Ponteiro para a pr
oxima adjac
encia
} Noh;
41
42
43
44
45
46
47
48
49
50
51
52
53
/*
es individu
a um vetor que armazenar
a informac
o
ais
* Estrutura principal. Ela ser
de
cada
vertice
al
e
m
de
um
ponteiro
para
todos
os
seus
adjacentes.
*
o de determinado noh, poder
a
a ser
* Assim, quando necessitar de uma informac
em de forncer todos os seus adjacentes indicados pelo
* acessado em O(1). Al
* ponteiro prox.
*/
typedef struct NohsIndividuais {
char visitado;
// Flag
int peso_atual;
// Dist
ancia deste n
o at
e o n
o de origem
struct Nohs * prox; // Ponteiro para a pr
oxima adjac
encia
} NohIndividual;
54
55
56
/*
57
58
59
60
61
62
63
64
da na forma
* Utilizou-se de uma pilha para imprimir a ordem de sa
* origem -> destino. O algoritmo naturalmente imprime de forma inversa, e por
* isso necessitou de uma pilha.
*/
typedef struct Pilhas {
int noh;
struct Pilhas * prox;
} Pilha;
65
66
67
/*
68
69
70
71
72
noh_pilha->noh = noh;
noh_pilha->prox = *s;
73
74
75
*s = noh_pilha;
76
77
78
79
80
81
82
83
84
/*
* Procedimento de retirar um item da pilha
*/
int popPilha (Pilha ** s) {
int retorno;
Pilha * proximo;
85
if (s != NULL) {
retorno = (*s)->noh;
proximo = (*s)->prox;
86
87
88
89
free(*s);
90
91
*s = proximo;
92
93
return retorno;
94
95
} else {
return -1;
}
96
97
98
99
100
101
102
103
104
105
106
/*
* Procedimento que realiza a retirada dos dados da pilha imprimindo cada um
* deles.
*/
void imprimePilha(Pilha * s, FILE * f) {
107
108
109
110
if (s == NULL) {
return;
} else {
111
112
113
114
115
while (s != NULL) {
fprintf(f, " %d", s->noh);
popPilha(&s);
}
}
fprintf(f, "\n");
116
117
118
119
120
121
122
123
124
/*
* Procedimento que retira todos os itens da pilha
*/
void esvaziaPilha(Pilha * s) {
125
126
127
if (s == NULL) {
return;
} else {
atual = s;
proximo = s->prox;
128
129
130
131
132
133
134
135
136
atual = proximo;
proximo = atual->prox;
137
138
139
140
free (atual);
141
142
143
144
145
146
147
148
149
150
/*
o que realiza a criac
o de um novo N
a
a
o
* Func
* preenchendo seus dados de acordo com os paramentros
*/
Noh * criaNovoNoh(int destino, int peso) {
151
152
153
154
es
// Atribui as novas informac
o
noh->noh_id
= destino;
noh->peso
= peso;
noh->prox
= NULL;
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/*
o de um novo n
a
o adjacente
* Procedimento que recebe dados para a criac
o
j
a
adicionando-o
na
sua
respectiva
lista
colocando-o
na
primeira
posic
a
*
* evitando a necessidade de percorrer a lista
*/
void criaNovaAdjacencia(NohIndividual * lista, int origem, int destino, int peso) {
170
171
172
173
174
// Cria um novo n
o
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*
o da base da lista de adjac
a
encia que e
* Procedimento que realiza a criac
es de cada n
o
o
* feita pela struct NohIndividual. Ela guarda informac
ancia da origem no momento.
* individual como se foi visitado e sua dist
em tem um ponteiro para a estrutura Noh que representa os Nohs adjacentes
* Tamb
o, informando o peso da aresta e seu identificador.
* deste n
/
*
NohIndividual * criaListaAdjacencia(int vertices) {
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*
ao da lista de ajdac
encia.
* Procedimento que realiza a impress
*/
void imprimeAdjacencia(NohIndividual * lista, int vertices, int arestas) {
210
211
212
213
printf("\nV
ertices: %d, Arestas: %d:\n", vertices, arestas);
214
215
216
217
218
219
220
221
222
atual = atual->prox;
}
printf("\n");
223
224
225
226
227
228
229
230
231
232
233
/*
es do
avel por ler o arquivo e recolher as informac
o
* Procedimento respons
grafo
nele
contido.
*
*/
NohIndividual * le_arquivo(char * diretorio, int * vertices, int * arestas) {
234
235
236
237
238
239
240
241
242
243
244
245
246
// L
e do arquivo o comando da linha
char comando = fgetc(bench);
247
248
249
// Enquando n
ao for final de arquivo
while (comando != EOF) {
250
251
252
253
254
255
256
o comando
// Verifica qual comando e
switch (comando) {
// Coment
arios ser
ao ignorados
case c:
// Le a linha inteira de comentario
while(fgetc(bench) != \n) ;
257
258
break;
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
// Le o n
umero de vertices e arestas
fscanf(bench, "%d %d", vertices, arestas);
278
279
280
281
282
283
284
285
286
287
288
289
case a:
// Verifica se ja tenha lido a quantidade de arestas antes de ler
// cada uma.
if (*vertices == 0 || * arestas == 00) {
printf("Nenhuma aresta ou v
ertice lido\n");
exit(-1);
}
290
291
292
293
294
295
` adjacencia
// Adiciona a aresta a
criaNovaAdjacencia(lista_adjacencia, origem_tmp, destino_tmp, peso_tmp);
296
297
298
// Quebra a linha
fgetc(bench);
299
300
301
break;
302
303
default:
break;
304
305
306
307
308
// Le o proximo comando
comando = fgetc(bench);
} //while
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
// Retorna a lista
return lista_adjacencia;
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
/*
o do algoritmod e Dijkstra. Ele realiza a
a
* Procedimento de inicializac
o dos valores de dist
a
ancia (peso_atual) de cada n
o e tamb
em
* inicializac
sinalizando
que
eles
ainda
n
a
o
foram
visitados.
*
ertices possuem o vertice de origem como o v
ertice
* Define que todos os v
* anterior.
em inicializa os valores do vertice fonte que dever
a ter propriedades
* Tamb
* diferente dos demais.
*/
void inicializaDijkstra(int fonte, int vertices, int * vertice_anterior, NohIndividual *
,
lista_adjacencia) {
int i;
// Inicializa todos dados individuais dos vertices
for (i = 0; i < vertices; i++) {
347
348
349
350
lista_adjacencia[i].peso_atual = -1;
lista_adjacencia[i].visitado = 0;
vertice_anterior[i] = fonte;
351
352
353
es do vertice fonte
// Redefine as informac
o
lista_adjacencia[fonte - 1].peso_atual = 0;
vertice_anterior[fonte - 1] = -1;
354
355
356
357
358
359
/*
360
ametro, como
* Procedimento que lista determinado vertice, passado por par
ves.
* vertice visitado, retirando da lista de dispon
o origem serja o primeiro a ser
* Procedimento utilizado para definir que o n
* descartado de uso.
*/
int extraiVertice(NohIndividual * lista_adjacencia, int id) {
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
/*
* Procedimento que realiza o relaxamento do algoritmo de Dijkstra.
o dos nos adjacentes alterando as distancias dos seus
a
* Realiza a verificac
* respectivos a procura de novos caminhos.
*/
void relaxamento(NohIndividual * lista, int vertice_anterior[], int origem_id) {
380
381
382
383
float peso_atual_temp;
384
385
386
387
388
// Recebe a dist
ancia do n
o atual
peso_atual_temp = lista[atual->noh_id - 1].peso_atual;
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
atual = atual->prox;
405
406
407
408
409
410
411
412
413
/*
o que realiza a procura de uma aresta ainda n
a
ao utilizada e que
* Func
vel de dist
ancia.
* tenha o menor custo poss
*/
int extraiVerticeMenosCustoso(NohIndividual * listaAdjacencia, int vertices, int
,
arestas) {
414
415
416
int i = 0;
417
418
// salta todos os n
os que n
ao podem ser utilizados como:
// n
os j
a visitados ou arestas inexistentes
while ((lista[i].visitado == 1 || lista[i].peso_atual < 1) && i < arestas)
i++;
419
420
421
422
423
424
425
426
// Se n
ao tiver excedido, define o primeiro dispon
vel como o menor para
es
// futuras comparac
o
int menor = i;
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
indexado de 1
// Lembrando que o ID e
menor++;
448
449
450
// Retorna o id do vertice
return menor;
451
452
}
else {
// Tratamento de erro
printf("N
ao foi encontrado uma nova aresta para operar.\nPrograma Finalizado.\n");
exit(2);
}
453
454
455
456
457
458
459
460
461
462
463
464
465
/*
o do usu
a
ario
* Procedimento final que imprime o caminho para melhor visualizac
ancia.
* bem como o valor total da dist
* IMPRIME O CAMINHO DE FORMA INVERSA: destino <- origem
*/
466
467
o primeiro n
// Diz que o destino e
o a ser percorrido anterior
int anterior = destino;
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
// Imprime tamb
em a dist
ancia do caminho
printf("\nDist
ancia pecorrida: %d\n.", lista_adjacencia[destino - 1].peso_atual);
484
485
486
487
488
489
490
491
492
493
494
/*
ao dos dados em arquivo para a an
alise.
* Procedimento que realiza a impress
ao, utiliza-se a pilha para que o caminho inverso seja impresso
* Para a impress
* de forma natural (origem -> destino)
*/
void imprimeTodosCaminhosArquivo(FILE * file, int vertices, int
,
vertice_anterior[vertices], NohIndividual lista_adjacencia[vertices], int origem)
,
{
495
496
int anterior, i;
497
498
499
500
501
502
503
504
505
506
inv
// Verifica se o v
ertice e
alido
if (vertice_anterior[i] != -1) {
507
508
509
510
es b
// Imprime as informac
o
asicas do arquivo como in
cio, fim e
// custo
fprintf(file, "[%d,%d](%d)", origem, i + 1, lista_adjacencia[i].peso_atual);
511
512
513
514
515
516
517
518
519
o pr
// Informa qual e
oximo item a ser colocado na pilha
anterior = vertice_anterior[i];
520
521
// Enquanto n
ao for a origem, adiciona os intermedi
arios na pilha
while(anterior != origem) {
pushPilha(&stack, anterior);
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
/*
* Algoritmo de Dijkstra
odigo do livro do Cormen.
* Baseado no pseudoc
*/
void dijkstra(int fonte, NohIndividual * lista_adjacencia, int vertices, int arestas,
,
int vertice_anterior[vertices]) {
544
545
546
547
548
// Inicializa as vari
aveis do algorimo
inicializaDijkstra(fonte, vertices, vertice_anterior, lista_adjacencia);
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
/*
avel
* Procedimento respons
o do algoritmo de
a
* execuc
*/
void desaloca(NohIndividual
// Cria ponteiros para a
Noh * deletar, * atual;
int i;
574
575
576
577
578
if ((*lista_adjacencia)[i].prox != NULL) {
deletar = (*lista_adjacencia)[i].prox;
atual = deletar->prox;
579
580
581
582
583
584
deletar = atual;
atual = atual->prox;
585
586
587
588
free(deletar);
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
if(argc == 2) {
// Vari
aveis de c
alculo de tempo
clock_t tempo_inicio, tempo_final;
double intervalo_real = 0;
604
605
606
607
608
609
610
611
612
613
614
if (vertices < 1)
exit(-1);
615
616
617
618
619
620
621
o
// Calcula o tempo de execuc
a
tempo_inicio = clock();
dijkstra(i + 1, lista_adjacencia, vertices, arestas, vertice_anterior);
tempo_final = clock();
622
623
624
625
626
627
628
629
630
631
632
633
634
o
// Persiste o tempo total de execuc
a
fprintf(tempos, "%f\n", intervalo_real);
635
636
desaloca(&lista_adjacencia, vertices);
637
638
639
640
641
}
o
// caso cont
ario, cancela a execuc
a
else {
printf("Argumentos Inv
alidos!\n");
exit(-1);
}
642
643
644
645
646
647
648
return (EXIT_SUCCESS);
649
650
/*
alise de Algoritmo
* Trabalho de Projeto e An
o - Turma 16.1
encia da Computac
a
* Mestrado em Ci
*
* Alunos (nome, matricula, e-mail):
Conrado
*
Danilo Santos Souza
16.1.10149 - danilo.gdc@gmail.com
*
Rodolfo Labiapari Mansur Guimar
aes 16.1.10163 - rodolfolabiapari@gmail.com
*
Thiago Schons
16.1.10186 - thiagoschons2@gmail.com
*
*
* Este arquivo executa o Algoritmo de Floyd Warshall.
*
*
* Para executar o arquivo utilize o comando:
"./nomeDoPrograma benchmark"
*
*
*
da est
a descrita da seguinte forma: [origem, destino](distancia) caminho
* Sa
Abaixo
e
exibido um exemplo
*
* [1,2](8) 1 4 2
* [1,3](9) 1 4 2 3
* [1,4](5) 1 4
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
27
28
29
30
31
32
33
34
35
36
37
/*
o das matrizes que o algoritmo necessita
a
* Procedimento que realiza a alocac
para
executar.
*
*/
void criaMatrizes(int *** distancia, int *** proximo, int vertices) {
// Instancia o primeiro n
vel das matrizes
*distancia = (int **) calloc(vertices, sizeof(int*));
= (int **) calloc(vertices, sizeof(int*));
*proximo
int i, j;
38
// Instancia os outros n
veis de cada matriz
for (i = 0; i < vertices; i++){
(*distancia)[i] = (int *) calloc(vertices, sizeof(int));
(*proximo)[i]
= (int *) calloc(vertices, sizeof(int));
}
39
40
41
42
43
44
45
46
47
48
49
50
51
(*distancia)[i][i] = 0;
52
53
54
55
56
/*
57
58
59
60
61
62
63
64
65
66
free(*distancia);
free(*proximo);
67
68
69
70
71
/*
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/*
ao dos dados de proximos
* Procedimento simples para a impress
*/
void imprimeProximos(int ** p, int vertices) {
int i, j;
printf("\n");
98
printf("
0:
1:
2:
3:
4:\n");
for (i = 0; i < vertices; i++){
for (j = 0; j < vertices; j++){
if (j == 0) {
printf("\033[1m\033[37m");
printf("%3.d: ", i);
printf("\033[0m");
}
printf("%3d ", p[i][j]);
}
printf("\n");
}
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
es do
avel por ler o arquivo e recolher as informac
o
* Procedimento respons
* grafo nele contido.
116
117
*/
void le_arquivo(char * diretorio, int *** matriz_distancia, int *** matriz_proximo, int
,
* vertices) {
118
119
120
121
122
123
124
125
126
127
128
129
130
// L
e do arquivo o comando da linha
char comando = fgetc(bench);
131
132
133
// Enquando n
ao for final de arquivo
while (comando != EOF) {
134
135
136
137
138
139
o comando
// Verifica qual comando e
switch (comando) {
// Coment
arios ser
ao ignorados
case c:
while(fgetc(bench) != \n) ;
140
141
break;
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// Le o n
umero de vertices e arestas
fscanf(bench, "%d %d", vertices, &arestas_temp);
161
162
163
164
165
166
o de matriz
// Flag indicando crianc
a
criou_matriz = 1;
167
168
break;
169
170
171
172
173
case a:
// Verifica se a matriz j
a foi criada
if (criou_matriz == 0) {
printf("Matriz n
ao criada!\n");
exit(-1);
174
175
176
count_arestas++;
177
178
// L
e a aresta do arquivo
fscanf(bench, "%d %d %d", &origem_tmp, &destino_tmp, &peso_tmp);
179
180
181
es na estrutura
// adiciona as informac
o
m_distancia[origem_tmp - 1][destino_tmp - 1] = peso_tmp;
182
183
184
185
186
187
188
189
// Quebra a linha
fgetc(bench);
break;
190
191
192
193
default:
194
195
break;
196
197
198
199
// Le o proximo comando
comando = fgetc(bench);
} //while
200
201
202
203
ao ao arquivo.\n");
exit(-1);
}
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
*matriz_distancia = m_distancia;
*matriz_proximo = m_proximo;
222
223
224
225
226
227
228
229
230
231
232
/*
o do usu
a
ario
* Procedimento final que imprime o caminho para melhor visualizac
ancia.
* bem como o valor total da dist
233
234
235
*/
void imprimeCaminho(FILE * file, int ** proximo, int ** distancia, int origem, int
,
destino) {
int caminho;
236
237
238
239
240
241
242
243
caminho = origem;
fprintf(file, " %d", caminho);
244
245
246
247
248
249
250
251
fprintf(file, "\n");
252
253
254
255
256
257
258
259
260
261
262
/*
* Algoritmo de Floyd Warshall.
* Baseado no livro do Cormen
*/
void floydWarshall(int ** m_distancia, int ** m_proximo, int vertices) {
int i, j, k;
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
if(argc == 2) {
// Vari
aveis de c
alculo de tempo
clock_t tempo_inicio, tempo_final;
double intervalo_real;
286
287
288
289
290
291
292
293
294
tempo_inicio = clock();
floydWarshall(m_distancia, m_proximo, vertices);
tempo_final = clock();
295
296
297
298
299
300
301
302
303
304
305
306
307
308
fclose(file_resultados);
fclose(tempos);
309
310
311
312
313
}
else {
printf("Argumentos Inv
alidos!\n");
exit(-1);
}
314
315
316
317
318
319
return (EXIT_SUCCESS);
320
321
Iteraca o
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
Bellman-Ford (s)
420.075737
406.778267
407.557925
408.164779
410.388223
417.608766
420.067948
423.579021
418.428915
419.904089
421.807243
422.480544
423.582300
421.011317
420.321190
419.562213
421.270161
421.778354
424.687490
419.337160
1.788467
1.799135
1.794091
1.787104
1.792932
1.831822
1.813434
1.830302
1.808650
1.805976
1.799024
1.817401
1.825335
1.853041
1.804325
1.786946
1.779710
1.793920
1.789469
1.782268
0.294253
0.299323
0.296152
0.287375
Dijkstra (s)
93.593469
93.155683
92.526349
92.922567
93.273871
93.188093
92.877990
93.481498
93.143297
92.777797
93.477219
93.329407
93.035231
93.371306
93.387431
93.327032
92.939897
93.259190
93.319218
93.211982
0.114640
0.114424
0.116044
0.113575
0.102091
0.104733
0.112657
0.119035
0.113649
0.113598
0.100087
0.117614
0.100887
0.116985
0.103362
0.114903
0.106953
0.113056
0.105754
0.103506
0.094997
0.096287
0.105085
0.093086
Floyd-Warshall (s)
149.329210
149.901242
148.299100
147.298434
147.728493
147.472081
147.649888
147.947385
147.331580
147.839273
147.339988
146.594208
147.465285
146.509349
147.291747
148.152184
146.992861
147.915625
146.724432
147.195511
0.118966
0.108971
0.112652
0.116166
0.114990
0.118885
0.110113
0.111056
0.112631
0.115899
0.117284
0.112459
0.114111
0.106702
0.106481
0.120008
0.120401
0.109078
0.107920
0.116683
0.116002
0.120532
0.117771
0.105078
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
rg300
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
768
floyd.gr
floyd.gr
floyd.gr
floyd.gr
floyd.gr
floyd.gr
floyd.gr
floyd.gr
floyd.gr
floyd.gr
floyd.gr
floyd.gr
floyd.gr
floyd.gr
floyd.gr
floyd.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
floyd-n.gr
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
0.288541
0.284657
0.289068
0.292276
0.287061
0.291642
0.293828
0.295847
0.286082
0.295810
0.297365
0.291032
0.289630
0.288871
0.288800
0.301314
0.289948
0.291154
0.298522
0.286472
0.289122
0.289937
0.294530
0.296994
0.292182
0.300920
0.299106
0.295046
0.295537
0.289527
0.293377
0.294441
0.304122
0.299738
0.293361
0.296438
0.091911
0.104280
0.103326
0.098178
0.104746
0.104740
0.105183
0.101016
0.105628
0.100730
0.093277
0.103123
0.101063
0.106986
0.093148
0.105861
0.117342
0.113558
0.106788
0.114823
0.106932
0.107975
0.103842
0.112194
0.112173
0.109862
0.116141
0.106996
0.112675
0.117593
0.116462
0.112277
0.098479
0.097173
0.097863
0.097455
0.098396
0.091062
0.094978
0.096971
0.092122
0.093879
0.099244
0.101335
0.096205
0.101148
0.099427
0.096725
0.095385
0.098785
0.098140
0.093913
Referencias
Cormen, T. H. (2002). Algoritmos: teoria e pratica. Elsevier.
Goldbarg, M. (2012). Grafos: Conceitos, algoritmos e aplicaco es. Elsevier Brasil.
Hernandes, F., Berton, L., and Castanho, M. J. d. P. (2009). O problema de caminho
mnimo com incertezas e restrico es de tempo. Pesquisa Operacional, 29(2):471488.
Netto, P. O. B. (2003). Grafos: teoria, modelos, algoritmos. Edgard Blucher.