==== Trabalho 1 (Heaps binários) ====
Entrega: 18/08/2014
=== Objetivos ===
* Implementar um heap binário e verificar a complexidade das operações experimentalmente.
* Implementar o algoritmo de Dijkstra usando o heap implementado.
* Comparar a complexidade teórica pessimista com a complexidade real. Em particular verificar que a complexidade real respeita o limite teórico.
* Avaliar o escalonamento do algoritmo Dijkstra.
=== Casos de teste ===
* Verificação da complexidade das operações: gerar casos de testes via um programa.
* Verificação da complexidade do Dijkstra: usar o gerador de casos de testes abaixo.
* Verificação do escalonamento: o caso de teste é o rede de trânsito de New York e dos EUA (distance), que pode ser baixado na [[http://www.dis.uniroma1.it/~challenge9/download.shtml|página do DIMACS challenge]].
* Para testar em geral: Gerar um número suficiente (>30) de pares aleatórias de vértices origem e destino e medir o tempo de execução e o número de operações "insert", "deletemin" e "decreasekey".
=== Observações ===
* Como o grafo possui 264346 vértices é necessário usar uma representação esparsa. Uma matriz de adjacência, em particular, não serve.
=== Convenções ===
* As implementações do algoritmo de Dijkstra devem aceitar um grafo no formato da DIMACS challenge na entrada padrão (stdin), os índices de dois vértices origem e destino na linha de comando e imprimir o valor do caminho mais curto na saída padrão (stdout). Caso não existo caminho entre os dois vértices imprimir "inf". Exemplo (em UNIX)
> ./dijkstra 1 2 < NY.gr
803
=== Gerador de casos de teste ===
/**
* \file gen.cpp
* \author Marcus Ritt
* \version $Id: emacs 2872 2009-01-31 01:46:50Z ritt $
* \date Time-stamp: <2011-08-24 15:17:49 ritt>
*/
#include
#include
using namespace std;
#include
#include
#include
using namespace boost;
// information stored in vertices
struct VertexInformation {
unsigned component;
};
// information stored in edges
struct EdgeInformation {
unsigned weight;
};
const unsigned maxweight = 1000;
// graph is an adjacency list represented by vectors
typedef adjacency_list Graph;
typedef graph_traits::vertex_descriptor Node;
typedef graph_traits ::edge_descriptor Edge;
int main(int argc, char *argv[]) {
assert(argc == 3);
unsigned n = atoi(argv[1]);
double p = atof(argv[2]);
srand48(time(0));
// (1) generate random graph
Graph g;
for(unsigned i=0; i dist(n);
vector pred(n);
dijkstra_shortest_paths(g,src,weight_map(get(&EdgeInformation::weight,g)).distance_map(&dist[0]).predecessor_map(&pred[0]));
cerr << "Distance between " << src+1 << " and " << dst+1 << " is " << dist[dst] << endl;
// (3) print out in DIMACS challenge format
cout << "p sp " << num_vertices(g) << " " << num_edges(g) << endl;
graph_traits::edge_iterator eb, ee;
for ( tie(eb, ee)=edges(g); eb != ee; eb++)
cout << "a " << source(*eb,g)+1 << " " << target(*eb, g)+1 << " " << g[*eb].weight << endl;
}
=== Leitura (Exemplo) ===
void read_dimacs(std::istream& in, unsigned& n, unsigned& m, MyGraph& a) {
std::string line="", dummy;
while (line.substr(0,4) != "p sp")
getline(in,line);
// (1) get nodes and edges
std::stringstream linestr;
linestr.str(line);
linestr >> dummy >> dummy >> n >> m;
a.resize(n);
unsigned i=0;
while (i> ac >> u >> v >> w;
// processar arco (u,v) com peso w
i++;
}
}
}
==== Trabalho 2 (Heaps de Fibonacci) ====
Entrega: 01/09/2014
=== Objetivos ===
* Implementar duas variantes de um heap de Fibonacci: i) a tradicional, com corte em cascata de acordo com a "marca" do vértice, ii) a randomizada.
* Verificar a complexidade das operações experimentalmente.
* Implementar o algoritmo de Dijkstra usando o heap implementado.
* Comparar a complexidade teórica pessimista do algoritmo de Dijkstra com a complexidade real usando as duas variantes do heap de Fibonacci. Em particular verificar que a complexidade real respeita o limite teórico.
* Comparar a complexidade com aquela do heap binário.
=== Casos de teste ===
* Os mesmos do trabalho 1.
=== Observações ===
* A variante randomizada de um heap de Fibonacci está descrita em [[http://arxiv.org/abs/1407.2569|Li,Peebles,Replacing Mark Bits with Randomness in Fibonacci Heaps]]. O método a) Continua um corte em cascata com probabilidade 1/2 e não usa "marcas". b) Reconstrói o heap em cada operação com probabilidade 1/n. A reconstrução insere todas chaves do heap atual num heap novo.
=== Convenções ===
* As mesmas do trabalho 1.
==== Trabalho 3 (Fluxo s-t máximo) ====
Entrega: 18/09/2013.
=== Objetivos ===
* Implementar o algoritmo push-relabel.
* Verificar a complexidade do algoritmo experimentalmente e comparar com a complexidade teórica O(n^3) e com o algoritmo do trabalho 2.
=== Casos de teste ===
* Um gerador de casos de teste em formato DIMACS em C é disponível {{http://www.inf.ufrgs.br/~mrpritt/aa/washington.c|aqui}}.
* Documentação:
To use: cc washington.c -o gengraph
gengraph function arg1 arg2 arg3
Command line arguments have the following meanings:
function: index of desired graph type
arg1, arg2, arg3: meanings depend on graph type
(briefly listed below: see code
comments for more info)
Mesh Graph: 1 rows cols maxcapacity
Random Level Graph: 2 rows cols maxcapacity
Random 2-Level Graph:3 rows cols maxcapacity
Matching Graph: 4 vertices degree
Square Mesh: 5 side degree maxcapacity
Basic Line: 6 rows cols degree
Exponential Line: 7 rows cols degree
Double Exponential 8 rows cols degree
DinicBadCase: 9 vertices
(causes n augmentation phases)
GoldBadCase 10 vertices
Cheryian 11 dim1 dim2 range
(last 2 are bad for goldberg's algorithm)
^ No. ^ Nome ^ Parâmetros ^ Descrição ^ n ^ m ^
| 1 | Mesh | r,c | Grade, 3 viz. 1 direita | rc+2 | 3r(c-1) |
| 2 | Random level | r,c | Grade, 3 viz. rand. 1 direita | rc+2 | 3r(c-1) |
| 3 | Random 2-level | r,c | Grade, 3 viz. rand. 2 direita | rc+2 | 3r(c-1) |
| 4 | Matching | n,d | Bipart. n-n, d viz. rand. | 2n+2 | n(d+2) |
| 5 | Square Mesh | d,D | Quadr. mesh dxd, grau D | d*d+2 | (d-1)dD+2d |
| 6 | BasicLine | n,m,D | Linha, grau D| nm+2 | nmD+2m |
| 7 | ExpLine | n,m,D | Linha, grau D | nm+2 | nmD+2m |
| 8 | DExpLine | n,m,D | Linha, grau D | nm+2 | nmD+2m |
| 9 | DinicBad | n | Linha | n | 2n-3|
| 10 | GoldBad | n | | 3n+3 | 4n+1 |
=== Convenções ===
* As implementações do algoritmo devem aceitar uma instância no formato {{http://lpsolve.sourceforge.net/5.5/DIMACS_maxf.htm|DIMACS}} na entrada padrão (stdin) e imprimir o valor do fluxo máximo na saída padrão (stdout).
=== Verificação ===
* O seguinte código determina o fluxo máximo. Para compilar: Usar C++ e {{http://www.boost.org|Boost}}.
/**
* \file maxflow.cpp
* \author Marcus Ritt
* \version $Id: emacs 2872 2009-01-31 01:46:50Z ritt $
* \date Time-stamp: <2009-03-23 17:52:25 ritt>
*
* Read a maximum flow problem in DIMACS format and output the maximum flow.
*
*/
#include
#include
using namespace std;
#include
#include
#include
using namespace boost;
// graph element descriptors
typedef adjacency_list_traits::vertex_descriptor DiNode;
typedef adjacency_list_traits::edge_descriptor Edge;
typedef unsigned Capacity;
struct VertexInformation {};
struct EdgeInformation {
Capacity edge_capacity;
Capacity edge_residual_capacity;
Edge reverse_edge;
};
typedef adjacency_list DiGraph;
int main(int argc, char *argv[]) {
// (0) read graph
DiGraph g;
DiNode s,t;
read_dimacs_max_flow(g,
get(&EdgeInformation::edge_capacity,g),
get(&EdgeInformation::reverse_edge,g),
s, t);
// (1) determine maximum flow
cout << edmonds_karp_max_flow(g, s, t,
capacity_map(get(&EdgeInformation::edge_capacity,g)).
residual_capacity_map(get(&EdgeInformation::edge_residual_capacity,g)).
reverse_edge_map(get(&EdgeInformation::reverse_edge,g))) << endl;
}
==== Trabalho 4 (Segmentação de imagens) ====
Entrega: 29/09/2014
=== Objetivos ===
* Implementar um algoritmo de segmentação de imagens como vista em aula.
* Usar a própria implementação do fluxo máximo na solução.
* Segmentar uma imagem em pele/não-pele com pesos de separação diferentes.
* Relatar o tempo de processamento & apresentar diferentes segmentações.
=== Casos de teste ===
* O caso de teste é uma imagem de si mesmo.
=== Convenções ===
* As implementações do algoritmo devem aceitar uma imagem no formata PPM plain na entrada padrão (stdin) e imprimir uma imagem no mesmo formato (com todos pixels não-pelo em preto) na saída padrão (stdout).
=== Códigos disponíveis ===
* Ler uma imagem PPM plain
// image data structure
enum { R = 0, G, B };
typedef unsigned short Color;
typedef multi_array Image;
// read an image im PPM (plain) format
void read_ppm(Image& im, istream& in) {
unsigned w,h,maxcolor;
string magic;
in >> magic >> w >> h >> maxcolor;
assert(magic == "P3");
im.resize(extents[h][w][3]);
for(unsigned i=0; i> im[i][j][R] >> im[i][j][G] >> im[i][j][B];
}
* Imprimir uma imagem PPM plain
cout << "P3 " << w << " " << h << " 255 " << endl;
for(unsigned i=0; i
* Converter qualquer imagem para PPM plain (usando {{http://www.imagemagick.org|Image Magick}})
convert -compress none myimage.ext myimage.ppm
* Determinar a probabilidade de pele ou não-pele (para obter valores inteiros multiplicar com p.ex. 10.000.000.000)
// mixture of Gaussians, according to Jones, Regh
const unsigned nG = 16;
const unsigned nV = 7;
double skin[nG][nV] = {
{ 73.53, 29.94, 17.76, 765.40, 121.44, 112.80, 0.0294 },
{ 249.71, 233.94, 217.49, 39.94, 154.44, 396.05, 0.0331 },
{ 161.68, 116.25, 96.95, 291.03, 60.48, 162.85, 0.0654 },
{ 186.07, 136.62, 114.40, 274.95, 64.60, 198.27, 0.0756 },
{ 189.26, 98.37, 51.18, 633.18, 222.40, 250.69, 0.0554 },
{ 247.00, 152.20, 90.84, 65.23, 691.53, 609.92, 0.0314 },
{ 150.10, 72.66, 37.76, 408.63, 200.77, 257.57, 0.0454 },
{ 206.85, 171.09, 156.34, 530.08, 155.08, 572.79, 0.0469 },
{ 212.78, 152.82, 120.04, 160.57, 84.52, 243.90, 0.0956 },
{ 234.87, 175.43, 138.94, 163.80, 121.57, 279.22, 0.0763 },
{ 151.19, 97.74, 74.59, 425.40, 73.56, 175.11, 0.1100 },
{ 120.52, 77.55, 59.82, 330.45, 70.34, 151.82, 0.0676 },
{ 192.20, 119.62, 82.32, 152.76, 92.14, 259.15, 0.0755 },
{ 214.29, 136.08, 87.24, 204.90, 140.17, 270.19, 0.0500 },
{ 99.57, 54.33, 38.06, 448.13, 90.18, 151.29, 0.0667 },
{ 238.88, 203.08, 176.91, 178.38, 156.27, 404.99, 0.0749 }
};
double noskin[nG][nV] = {
{ 254.37, 254.41, 253.82, 2.77, 2.81, 5.46, 0.0637 },
{ 9.39, 8.09, 8.52, 46.84, 33.59, 32.48, 0.0516 },
{ 96.57, 96.95, 91.53, 280.69, 156.79, 436.58, 0.0864 },
{ 160.44, 162.49, 159.06, 355.98, 115.89, 591.24, 0.0636 },
{ 74.98, 63.23, 46.33, 414.84, 245.95, 361.27, 0.0747 },
{ 121.83, 60.88, 18.31, 2502.24, 1383.53, 237.18, 0.0365 },
{ 202.18, 154.88, 91.04, 957.42, 1766.94, 1582.52, 0.0349 },
{ 193.06, 201.93, 206.55, 562.88, 190.23, 447.28, 0.0649 },
{ 51.88, 57.14, 61.55, 344.11, 191.77, 433.40, 0.0656 },
{ 30.88, 26.84, 25.32, 222.07, 118.65, 182.41, 0.1189 },
{ 44.97, 85.96, 131.95, 651.32, 840.52, 963.67, 0.0362 },
{ 236.02, 236.27, 230.70, 225.03, 117.29, 331.95, 0.0849 },
{ 207.86, 191.20, 164.12, 494.04, 237.69, 533.52, 0.0368 },
{ 99.83, 148.11, 188.17, 955.88, 654.95, 916.70, 0.0389 },
{ 135.06, 131.92, 123.10, 350.35, 130.30, 388.43, 0.0943 },
{ 135.96, 103.89, 66.88, 806.44, 642.20, 350.36, 0.0477 }
};
double skin_value(Color R, Color G, Color B) {
double r = 0;
for(unsigned i=0; i
==== Trabalho 5 (Teste de primalidade) ====
Entrega: 26/11/2014
=== Objetivos ===
* Implementar o teste de primalidade de Miller & Rabin
* Analisar a complexidade do algoritmo em função do tamanho da entrada (log_2(n)) experimentalmente.
* Analisar a probabilidade de responder erradamente "sim" experimentalmente.
=== Casos de teste ===
* Números inteiros positivos aleatórios.
=== Convenções ===
* As implementações do algoritmo devem ler um número decimal com até 1000 dígitos da entrada padrão e imprimir "s" na saída padrão caso o número é considerado primo e "n" caso contrário.
=== Dicas ===
* Usa a biblioteca {{http://gmplib.org|GNU MP}} para representar números grandes. Ela tem interfaces para {{http://en.wikipedia.org/wiki/GNU_Multiple_Precision_Arithmetic_Library#Language_bindings|diversas linguagens}} de programação e é simples de usar. Um exemplo em C++:
#include
using namespace std;
#include
int main(int argc, char *argv[]) {
mpz_class a;
cin >> a;
cout << "O quadrado de " << a << " é " << a*a << endl;
}