Ferramentas de Utilizador

Ferramentas de Site


inf05016:2014-2-trabalhos

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 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 <mrpritt@inf.ufrgs.br> 
 *   \version $Id: emacs 2872 2009-01-31 01:46:50Z ritt $
 *   \date Time-stamp: <2011-08-24 15:17:49 ritt>
 */
 
#include <iostream>
#include <cassert>
using namespace std;
 
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/connected_components.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
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<vecS, vecS, directedS,VertexInformation,EdgeInformation> Graph;
typedef graph_traits<Graph>::vertex_descriptor Node;
typedef graph_traits <Graph>::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<n; i++)
    add_vertex(g);
 
  for(unsigned i=0; i<n; i++)
    for(unsigned j=0; j<n; j++)
      if (i != j && drand48() < p) {
        Edge e = add_edge(i,j,g).first;
	g[e].weight = lrand48()%maxweight;
      }
 
 
  // (2) print example path
  unsigned src = lrand48()%num_vertices(g);
  unsigned dst = lrand48()%num_vertices(g);
 
  vector<unsigned> dist(n);
  vector<unsigned> 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<Graph>::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<m) {
    getline(in,line);
    if (line.substr(0,2) == "a ") {
      std::stringstream arc(line);
      unsigned u,v,w;
      char ac;
      arc >> 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 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 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 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 Boost.
/**
 * \file maxflow.cpp
 *   \author Marcus Ritt <mrpritt@inf.ufrgs.br>
 *   \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 <iostream>
#include <cstring>
using namespace std;
 
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/read_dimacs.hpp>
#include <boost/graph/edmonds_karp_max_flow.hpp>
using namespace boost;
 
// graph element descriptors
typedef adjacency_list_traits<vecS,vecS,directedS>::vertex_descriptor DiNode;
typedef adjacency_list_traits<vecS,vecS,directedS>::edge_descriptor Edge;
 
typedef unsigned Capacity;
struct VertexInformation {};
struct EdgeInformation {
  Capacity edge_capacity;
  Capacity edge_residual_capacity;
  Edge reverse_edge;
};
 
typedef adjacency_list<vecS,vecS,directedS,VertexInformation,EdgeInformation> 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<Color,3> 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<h; i++)
    for(unsigned j=0; j<w; j++)
      in >> 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<h; i++) {
    for(unsigned j=0; j<w; j++)
      cout << im[i][j][R] << " " << im[i][j][G] << " " << im[i][j][B] << " ";
      cout << endl;
  }
  • Converter qualquer imagem para PPM plain (usando 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<nG; i++) {
    double t;
    t = pow(double(R)-skin[i][0],2)/skin[i][3] + pow(double(G)-skin[i][1],2)/skin[i][4] + pow(double(B)-skin[i][2],2)/skin[i][5];
    t = skin[i][6]*exp(-t/2) / (pow(2*M_PI,1.5) * sqrt(skin[i][3]*skin[i][4]*skin[i][5]));
    r += t;
  }
  return r;
}
 
double noskin_value(Color R, Color G, Color B) {
  double r = 0;
  for(unsigned i=0; i<nG; i++) {
    double t;
    t = pow(double(R)-noskin[i][0],2)/noskin[i][3] + pow(double(G)-noskin[i][1],2)/noskin[i][4] + pow(double(B)-noskin[i][2],2)/noskin[i][5];
    t = noskin[i][6]*exp(-t/2) / (pow(2*M_PI,1.5) * sqrt(noskin[i][3]*noskin[i][4]*noskin[i][5]));
    r += t;
  }
  return r;
}

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 GNU MP para representar números grandes. Ela tem interfaces para diversas linguagens de programação e é simples de usar. Um exemplo em C++:
#include <iostream>
using namespace std;
 
#include <gmpxx.h>
 
int main(int argc, char *argv[]) {
  mpz_class a;
  cin >> a;
  cout << "O quadrado de " << a << " é " << a*a << endl;
}
inf05016/2014-2-trabalhos.txt · Esta página foi modificada pela última vez em: 2014/11/05 13:40 por marcus