Unidade: Tabela Hash e Grafos. Unidade I:

Documentos relacionados
Tabela Hash. Prof. Msc. Mariella Berger

Bit (Binary Digit) é a menor unidade de armazenamento de informação dentro do computador; Byte é a união de 8 bits capaz de representar 256

Estrutura de Dados. Unidade VI. -Tabela Hash -Grafos. Prof. Ms. Amilton Souza Martha

HASHING. Katia Guimarães. 1

Tabelas de dispersão/hash

Tabela Hash. Disciplina de Algoritmos e Estrutura de Dados III. Prof. Marcos Antonio Schreiner 15/05/2015

Hashing: conceitos. Hashing

TAD dicionário. Métodos do TAD dicionário:

Tabelas Hash. Jeane Melo

Matemática Discreta 12

Dicionários. TAD Orientado a conteúdo

Algoritmos e Estrutura de Dados II. Árvore. Prof a Karina Oliveira.

UNIP - Ciência da Computação e Sistemas de Informação. Estrutura de Dados. AULA 8 Grafos. Estrutura de Dados 1

Tabelas de Dispersão. Estrutura de Dados e Algoritmos

S U M Á R I O. Definição de Estrutura de Dados. Vetores. Listas Ligadas. Pilhas. Filas

Algoritmos de pesquisa. Tabelas de dispersão/hash

Métodos de Busca Parte 2

Introdução. Coleção HashMap Classe Java. Interesse

Unidade: Estruturas de Dados Não Lineares. Unidade I:

Estruturas de Dados Tabelas de Espalhamento

TABELAS DE DISPERSÃO/HASH

Estrutura de Dados e Algoritmos e Programação e Computadores II. Aula 10: Introdução aos Grafos

Hashing. Prof. Josenildo Silva IFMA 2014

Dicionários. Prof. César Melo

Linguagens Formais e Autômatos. Autômatos Finitos Determinísticos (AFD)

Hashing (Tabela de Dispersão)

Programação Orientada a Objectos - P. Prata, P. Fazendeiro

Tabelas de hash Acabamos de estudar como implementar uma tabela hashing aberta e estudaremos agora como implementar uma tabela hashing fechada ou

Conceitos Básicos da Teoria de Grafos

Métodos de Busca. Parte 2. ICC2 Prof. Thiago A. S. Pardo. Baseado no material do Prof. Rudinei Goularte

Introdução Métodos de Busca Parte 2 - Hashing

Pesquisa em Memória Primária. Prof. Jonas Potros

Algoritmos e Estrutura de Dados II. Árvore AVL. Prof Márcio Bueno. / Material da Prof a Ana Eliza

MC3305 Algoritmos e Estruturas de Dados II. Aula 02 Hashing. Prof. Jesús P. Mena-Chalco.

ESTRUTURAS DE DADOS E ALGORITMOS TABELA HASH

Complexidade de Algoritmos

Web site. Profa. Patrícia Dockhorn Costa.

Programação Orientada a Objetos

Árvores. Fabio Gagliardi Cozman. PMR2300 Escola Politécnica da Universidade de São Paulo

TABELA HASH. Prof. André Backes. Princípio de funcionamento dos métodos de busca

A inserção da chave 6 da árvore acima resulta na árvore abaixo.

SCC0601 Introdução à Ciência da Computação II. Prof. Lucas Antiqueira

Compiladores. Prof. Bruno Moreno Aula 8 02/05/2011

O estudo utilizando apenas este material não é suficiente para o entendimento do conteúdo. Recomendamos a leitura das referências no final deste

Linguagens Regulares. Prof. Daniel Oliveira

Tabelas de Dispersão. Algoritmos e Estruturas de Dados Verão Cátia Vaz 1

Edital de Seleção 032/2016 PROPESP/UFAM. Prova de Conhecimento. Caderno de Questões

Árvores. Thiago Martins, Fabio Gagliardi Cozman. PMR2300 / PMR3201 Escola Politécnica da Universidade de São Paulo

ÁRVORES E ÁRVORE BINÁRIA DE BUSCA

Edital de Seleção 055/2017 PROPESP/UFAM. Prova de Conhecimento. Caderno de Questões

Estruturas de Dados Aula 1: Introdução e conceitos básicos 28/02/2011

Tabelas Hash & Outras Estruturas

Conceito Básicos da Teoria de Grafos

Tabelas Hash. informação, a partir do conhecimento de sua chave. Hashing é uma maneira de organizar dados que:

Edital de Seleção 024/2017 PROPESP/UFAM. Prova de Conhecimento. Caderno de Questões

Grafos IFRN. Robinson Alves

Desenvolvimento e Projeto de Aplicações Web

Tabelas Hash. Prof. Túlio Toffolo BCC202 Aulas 23 e 24 Algoritmos e Estruturas de Dados I

1 OBJETIVOS 2 HORÁRIO DE AULAS 3 PROGRAMA

Algoritmos e Estruturas de Dados

Teoria dos grafos. FATEC Carapicuíba Augusto de Toledo Cruz Junior

Orientação a Objetos AULA 09

pior caso nas operações de manipulação é O(N) pior caso surge sistematicamente emusos correntes (ex: elementos previamente ordenados)

Tabelas de símbolos e de distribuição AULA 23. Tabela de símbolos. Um exemplo simples. Interface

Estrutura de Dados. Estrutura de Dados Recursividade. Exemplo: 5! = 5 x 4! 4! = 4 x 3! 3! = 3 X 2! 2! = 2 x 1! 1! = 1 x 0!

Universidade Estadual de Mato Grosso do Sul Bacharelado em Ciência da Computação Algoritmos e Estruturas de Dados II Prof. Fabrício Sérgio de Paula

Edital de Seleção 053/2016 PROPESP/UFAM. Prova de Conhecimento. Caderno de Questões

ESTRUTURA DE DADOS E ALGORITMOS. Hashing (Tabela de Dispersão) Cristina Boeres

Agenda. Ideia. Buscar um item em um array. Função hash. Função hash. Hash Table. Introdução Definição Hash Table. Métodos de resolução de conflitos

IFRN. Introdução à Teoria dos Grafos. Prof. Edmilson Campos

Transformação de Chave. (Hashing)

13 Hashing (parte 2) SCC201/501 - Introdução à Ciência de Computação II

Árvores. Thiago Martins, Fabio Gagliardi Cozman. PMR2300 / PMR3201 Escola Politécnica da Universidade de São Paulo

Prova 2 PMR3201 1o. semestre 2015 Prof. Thiago Martins

Teoria dos Grafos. Edson Prestes

Grupo 2 - Implementação de uma Classe Simples

Hashing Externo. SCC-503 Algoritmos e Estruturas de Dados II. Thiago A. S. Pardo M.C.F. de Oliveira Cristina Ciferri

HASHING Hashing Motivação - Acesso Direto:

ICET CURSO: Ciência da Computação e Sistemas de Informação (Estrutura de Dados) Estudos Disciplinares Campus: Data: / / Nome:

Teoria dos Grafos AULA 1

Programação Orientada a Objetos

Estrutura de Dados e Arquivos Ementa Objetivo Geral Conteúdo Avaliação

Prova 2 PMR3201 1o. semestre 2016 Prof. Thiago Martins

Aula 3: Autômatos Finitos

As Listas são simples de se implementar, mas, com um tempo médio de acesso T = n/2, tornando-se impraticáveis para grandes quantidades de dados.

a n Sistemas de Estados Finitos AF Determinísticos

Teoria dos Grafos AULA 1

Algoritmos e Estrutura de Dados II. Árvore AVL. Prof a Karina Oliveira. Fonte: Prof a Ana Eliza

Transformação de Chave - Hashing

Grafos COM11087-Tópicos Especiais em Programação II

Hashing. Prof Márcio Bueno. /

Hashing. Rafael Nunes LABSCI-UFMG

Estrutura de Dados. Carlos Eduardo Batista. Centro de Informática - UFPB

Implemente a função de remoção de uma chave na tabela hashing fechada em questão. void remover(tabela tabela, int n) {

Arvores binárias. Fonte: PF 14 pf/algoritmos/aulas/bint.html

HASHING HASHING 6/10/2008

Vamos considerar um arquivo de dados que armazena uma lista de alunos. Cada registro é um objeto com um número de matrícula e um nome.

Transcrição:

Unidade I: 0

Unidade: Tabela Hash e Grafos 1 Tabelas Hash Um dos maiores problemas encontrados quando se estuda a alocação de estruturas de dados é o tempo de resposta da pesquisa de uma chave em um conjunto de elementos. Quando pensamos em pesquisa de uma chave, na maioria das vezes optamos por basear na comparação de um elemento a ser pesquisado (k) com todas as chaves da estrutura (ou somente algumas, como nas árvores). É interessante também se analisar uma alternativa, que é baseada no cálculo da posição do registro através de funções aritméticas f(k) que tem como argumento o valor da chave k, que permite que o tempo de pesquisa seja independente no número de elementos na estrutura. A tabela Hash, também conhecida como Tabela de Dispersão ou o termo em inglês Hash Table, é uma estrutura de dados não linear. Ao contrário das pilhas, filas e listas ordenadas, onde os elementos estão dispostos um após o outro, as tabelas Hash são estruturas de dados não lineares, assim como as árvores. Os elementos são inseridos, removidos ou pesquisados em uma posição determinada por uma função de hashing (ou função de dispersão) que, conforme uma chave de entrada, determina qual a posição que o elemento deve seguir. Uma das vantagens da utilização de tabelas Hash é o tempo constante para inserções, remoções e pesquisas visto que o elemento é acessado diretamente no endereço onde deve estar e não há a necessidade de varrer a estrutura. Porém, essa melhora na busca é contrabalanceada com um aumento de memória gasta. A função de Hashing pode, por vezes, gerar o mesmo endereço para chaves de entrada diferentes. Esse processo se chama colisão. Uma função de hashing perfeita não deve gerar colisões, mas na maioria dos casos, as colisões devem ser tratadas. O número gerado por uma função de hashing é o hash code. 1

Exemplo de Hashing Suponha que 1. O espaço de chaves são os números inteiros de quatro dígitos, e 2. Deseja-se traduzi-los no conjunto {0, 1,..., 7}. (8 posições disponíveis) Uma hash function poderia ser: f(x) = (5 * x) mod 8. Se o conjunto de dados for constituído pelos anos: 1055, 1492, 1776, 1812, 1918 e 1945, a hash function f (x) = (5 * x) mod 8 gerará o seguinte mapeamento: Ex: f (1055) = (5 1055) mod 8 = 5275 mod 8 = 3 Índice 0 1 2 3 4 5 6 7 Chave 1776 1055 1492 1812 1945 1918 Ex: f (2002) = (5 2002) mod 8 = 10010 mod 8 = 2 1.1 - Colisões No exemplo anterior dizemos que entre as chaves 1492 e 1812 ocorreu uma colisão, isto é estas duas chaves geraram o mesmo hash code, ou seja, foram mapeadas no mesmo índice. O desejável seria que a função fosse injetiva, de forma a evitar colisões, mas como isso é muito difícil, há vários esquemas para trabalhar a ocorrência de colisões. Há duas grandes classes de abordagens: 1. Closed Address Hashing (endereçamento fechado) 2. Open Address Hashing (endereçamento aberto) Closed Address Hashing (endereçamento fechado) Closed Address Hashing ou hashing encadeado é a forma mais simples de tratamento de colisão. Cada entrada H[i] da tabela hash é uma lista encadeada, cujos elementos têm hash code i. Para inserir um elemento na tabela: 1. Compute o seu hash code i, e 2. Insira o elemento na lista ligada H[i]. 2

Embora uma função hash bem escolhida promova um bom balanceamento, não se pode garantir que as listas terão tamanhos próximos. Seria possível substituir a lista ligada por estruturas mais eficientes de busca, como árvores balanceadas, mas isso não se faz na prática. Open Address Hashing (endereçamento aberto) É uma estratégia para guardar todas as chaves na tabela, mesmo quando ocorre colisão. H[i] contém uma chave, ao invés de um link. Em caso de colisão, um novo endereço é computado. Esse processo é chamado rehashing. Tem a vantagem de não usar espaço extra. Rehashing por Linear Probing A forma mais simples de rehashing é linear probing. Se o hash code f (K) = i, e alguma outra chave já ocupa a posição H[i], então a próxima posição disponível na tabela H será ocupada pela chave K : rehash (i) = (i+1) mod h. Ex: Se o conjunto de dados for constituído pelos anos: 1055, 1492, 1776, 1812, 1918 e 1945, a hash function f (x) = (5 x) mod 8, e usando linear probing na colisão rehash (4) = (4+1) mod 8 = 5 rehash (5) = (5+1) mod 8 = 6 3

rehash (6) = (6+1) mod 8 = 7 Note que: 1. É possível que uma posição i da tabela Hash já esteja ocupada com alguma chave cujo hash code é diferente de f (K). 2. Rehashing por linear probing não depende do valor da chave K. Para recuperar uma chave: 1. Compute o valor de f (K) = i. 2. Se H[i] está vazia, então K não está na tabela. 3. Se H[i] contém alguma chave diferente de K, Então, compute rehash (i) = i1 = (i + 1) mod h. 4. Se H[i1] está vazia, então K não está na tabela. Senão, se H[i1] contém alguma chave diferente de K, então, rehash (i1), etc... Rehashing por Linear Probing pode trazer sérios problemas de colisão se houver uma alta taxa de ocupação na tabela Hash. Para um bom desempenho é importante manter a taxa de ocupação da tabela próxima a 0,5 (50% do espaço). Rehashing por Double Hashing Ao invés de fazer os incrementos de 1 invariavelmente, os incrementos são feitos por um valor d, que depende da chave K. Ex: d = HashIncr (K) rehash (j, d) = (j+d) mod h. Ex: Conjunto de dados: 1055, 1492, 1776, 1812, 1918 e 1945 Hash function f (x) = (5 x) mod 8 Colisões resolvidas por double hashing com HashIncr(K) = (K mod 7) + 1 f (1812) = (5 1812) mod 8 = (9060 mod 8) = 4 HashIncr (1812) = (1812 mod 7) + 1 = 6 + 1 = 7 rehash (4, 7) = (4 + 7) mod 8 = 3 rehash (3, 7) = (3 + 7) mod 8 = 2 4

Removendo Elementos da Tabela A remoção é uma operação delicada em tabelas Hash. Usa-se um bit para indicar se a posição está, de fato, ocupada por um elemento válido da tabela, ou se o dado que se encontra naquela entrada não faz mais parte da estrutura. Exemplo em Java Um mapa (classe Map) é uma coleção de pares ordenados que mapeiam um valor associado a uma chave em um mapa. Em Java temos duas classes que implementam mapas com tabelas Hash. A classe mais antiga é a HashTable, que é sincronizada e não aceita null na chave ou no valor e está desde a versão Java 1.0. O problema de ser sincronizada é que não podemos inserir e remover elementos simultâneos (métodos put e get). A outra classe mais atual é a HashMap, que por sua vez não é sincronizada (permite inserção e remoção simultânea), aceita null tanto no valor como na chave e está presente desde a versão 1.2 do Java. Ambas as classes possuem algumas modificações durante as versões até a versão atual 1.6 do Java, porém a mais recomendada é a utilização da classe HashMap. É possível gerar uma versão sincronizada da classe HashMap, de modo a utilizar a sincronização apenas onde se faz necessário, fazendo: Map seumapasincronizado = Collections.synchronizedMap(seuMapa); O uso de mapas sincronizados só é interessante quando for necessário o uso de multi threads na sua implementação de mapas. A classe HashMap implementada o endereçamento fechado para tratamento de colisões. Os pricipais métodos da classe HashMap são: 5

boolean containskey(object key) Retorna true se o mapa contém um valor mapeado para essa chave boolean containsvalue(object value) Retorna true se o mapa contém uma chave mapeada para o valor especificado Object get(object key) Retorna o valor que essa chave mapeia na tabela boolean isempty() Retorna true se a tabela está vazia Object put(object key, Object value) Associa um determinado valor a uma chave na tabela Object remove(object key) Remove o mapeamento dessa chave para um valor da tabela int size() Retorna o número de chaves mapeadas na tabela Exemplo de código: HashMap ht = new HashMap(); //Insere um elemento na tabela hash associado a uma chave ht.put("01", "A"); ht.put("02", "B"); //Mostra a quantidade de valores mapeados na tabela System.out.println(ht.size()); //Recupera o valores baseado na chave System.out.println(ht.get("01")); //Remover um elemento da tabela baseado na chave ht.remove("01"); 6

//Retorna true se a tabela contém a chave especificada if(ht.containskey("01")) System.out.println("Achei a chave 01"); //Retorna true se a tabela contém o valor especificado if(ht.containsvalue("b")) System.out.println("Achei o valor B"); HashTable e HashMap trabalham de maneiras diferentes no espalhamento dos valores. Suas funções de hashing não garantem uma ordenação dos elementos. Veja o exemplo: Map ht = new Hashtable(); Map hs = new HashMap(); String[] chaves = {"um", "dois", "tres", "quatro", "cinco", "seis", "sete", "oito", "nove", "dez"}; String[] valores= {"eins", "zwei", "drei", "vier", "fuenf", "sechs", "sieben", "acht", "neun", "zehn"}; for (int i = 0; i < chaves.length; ++i) { ht.put (chaves[i], valores[i]); hs.put (chaves[i], valores[i]); } System.out.println ("Hashtable: " + ht); System.out.println ("HashMap: " + hs); 7

2 - Grafos A teoria dos grafos é a parte da matemática que se dedica a estudar as relações entre entidades (objetos), que possuem características relevantes si. O primeiro grafo que se tem notícia data de 1736, quando Euler propôs um problema conhecido como problema do carteiro chinês. Na Prússia, perto da cidade de Konigsberg, no rio Pregel, sete pontes ligavam duas ilhas entre si e ao continente. O problema era encontrar um caminho que não cruzasse uma mesma ponte duas vezes, tendo como ponto de partida e chegada o mesmo pedaço de terra. Vimos até agora as estruturas de dados lineares (Pilhas, Filas e Listas Ordenadas) e estruturas não lineares (árvores e tabelas hash). Aprendemos que as árvores são estruturas de dados baseadas em nós, onde cada nó na árvore pode ter qualquer quantidade de nós filhos, cujos filhos se comportam da mesma maneira de forma recursiva. 8

O que percebemos também é que uma árvore possui certas regras que limitam a sua formação. Vimos que os filhos de cada nó formam estruturas disjuntas, que não possuem vínculos com as outras. Desse modo, cada nó só pode possuir um pai. Se permitirmos que cada nó possa se conectar com qualquer outro nó da estrutura, então temos um Grafo. De maneira simples, um grafo é uma estrutura baseada em nós onde cada nó pode se conectar a qualquer outro nó na estrutura. Portanto, podemos dizer que uma árvore é um tipo de grafo onde as regras de formação são mais específicas. Um grafo é composto de duas partes, sendo a primeira os nós (chamados vértices), que contem a informação e a segunda as arestas, que basicamente são as linhas que conecta um nó ao outro. Um grafo G(V,A) é definido pelo par de conjuntos V e A, onde: V - conjunto não vazio: os vértices ou nodos do grafo; A - conjunto de pares ordenados a=(v,w), v e w Î V: as arestas do grafo. Seja, por exemplo, o grafo G(V,A) dado por: V = { p p é uma pessoa } A = { (v,w) < v é amigo de w > } Esta definição representa toda uma família de grafos. Um exemplo de elemento 9

desta família (ver G1) é dado por: V = { Maria, Pedro, Joana, Luiz } A = { (Maria, Pedro), (Joana, Maria), (Pedro, Luiz), (Joana, Pedro) } 2.1 - Tipos de Grafos Dependendo da aplicação, arestas podem ou não ter direção, pode ser permitido ou não arestas ligarem um vértice a ele próprio e vértices e/ou arestas podem ter um peso (numérico) associado. Se as arestas têm uma direção associada (indicada por uma seta na representação gráfica) temos um grafo direcionado, ou dígrafo. Veja um exemplo de dígrafo: V = { p p é uma pessoa da família Castro } A = { (v,w) < v é pai/mãe de w > } Um exemplo de deste grafo é: V = { Emerson, Isadora, Renata, Antonio, Rosane, Cecília, Alfredo } A = {(Isadora, Emerson), (Antonio, Renata), (Alfredo, Emerson), (Cecília, Antonio), (Alfredo, Antonio)} Existem muitos tipos diferentes de implementação de grafos, vamos ver 10

alguns mais importantes: 1) Grafos Bidirecionais: nesse caso, as arestas sempre possuem direção de ida e volta. 2) Grafos Unidirecionais: esse tipo de grafo é um pouco mais limitado do que o grafo bidirecional pois cada aresta tem apenas uma direção. É possível simular um grafo bidirecional utilizando grafos unidirecionais, que apesar de parecer mais trabalhoso, proporciona um maior controle da estrutura. Veja o modelo abaixo: Um grafo direcionado, ou dígrafo, G é descrito pelo par (V, E), onde: V é um conjunto finito, não vazio, de nós, chamados vértices de G; E é um conjunto finito de pares ordenados de vértices, chamados arestas ou arcos; 11

Exemplo: Seja G1 = (V1, E1) composto por 4 vértices e 6 arestas. G 1 a b V1 = { a, b, c, d } e E1 = { (a, b), (a, c), (b, c), (c, a), (c, d), (d, d) } c d 2.2 - Utilizando grafos para uma máquina de estados finita Uma máquina de estados finitos ou Autômatos Finitos é uma modelagem de um comportamento, composto por estados, transições e ações. Um estado armazena informações sobre o passado, isto é, ele reflete as mudanças desde a entrada num estado, no início do sistema, até o momento presente. Uma transição indica uma mudança de estado e é descrita por uma condição que precisa ser realizada para que a transição ocorra. Uma ação é a descrição de uma atividade que deve ser realizada num determinado momento. Máquinas de Estado Finito podem ser utilizadas no processo de reconhecimento de palavras dentro de um compilador ou para mapear o comportamento de um personagem dentro do cenário de um jogo. 2.2.1 - Definição formal de máquina de estado finito Um Autômato Finito pode ser determinístico (AFD), ou seja, para cada símbolo de entrada (ação) só existe um caminho a seguir ou não determinístico (AFND), que nesse caso, para um mesmo símbolo de entrada existem mais do que uma possibilidade de transição de estado. Um AFD é composto por uma quíntupla, M = (, Q,, q0, F), onde: : alfabeto de símbolos de entrada; Q: conjunto finito de estados do autômato; : função programa ou função de transição (parcial) : Q Q q0: estado inicial (q0 Q) F: conjunto de estados finais ou estados de aceitação (F Q) Exemplo: Autômato que reconhece a linguagem de números binários com quantidade ímpar de 1s. 12

M = (, Q,, qo, F) Q = { qo, q1 }, = { 0, 1 }, F = { q1 } Forma Tabular : Forma Gráfica : X q o q 1 0 1 q o q 1 q 1 q o 0 q o 1 1 q 0 A forma gráfica, chamada diagrama de transição de estados, é a representação mais apropriada para descrever um AF. Esta representação é um grafo direcionado rotulado com um nó para cada estado e uma aresta rotulada. Genericamente, uma aresta sai de um nó q para um nó p com o rótulo a, ou seja: (q, a) = p Do exemplo (qo, 0) = qo anterior (qo, 1) = q1 (q1, 0) = q1 (q1, 1) = qo 13

Exemplo 1: Construir um AFD que reconhece a linguagem a*. M = (, Q,, q o, F) Q = { q o }, = { a }, F = { q 0 } Forma Tabular : Forma Gráfica : a a q q o q o o Exemplo 2: Construir um AFD que reconhece a linguagem aa*. M = (, Q,, q o, F) Q = { q o, q 1 }, = { a }, F = { q 1 } Forma Tabular : Forma Gráfica : a a a q o q 1 q o q q 1 q 1 14

Exemplo 3: Construir um AFD que reconhece a linguagem (abb*a)*. M = (, Q,, q o, F) Q = { q o, q 1, q 2 }, = { a, b }, F = { q 0 } Forma Tabular : Forma Gráfica : a b a q o q 1 q 1 q 2 q 2 q 0 q 2 q o b q 1 a q 2 b 15

Referências PEREIRA, S. L. Estruturas de Dados Fundamentais: Conceitos e Aplicações. 9. ed. São Paulo: Erica, 2001. MAIN, M. Data Structures & Other Objects Using C++. 3. ed. Boston: Pearson Education., 2005. PENTON, R. Data Structures For Game Programmers. Ohio, Usa: Focal Press, 2003. SZWARCFITER, Jayme Luiz, Estruturas de Dados e Seus Algoritmos, LTC, Rio de Janeiro, 1994 16

17 Responsável pelo Conteúdo: Prof. Ms. Amilton Souza Martha Revisão Textual: Prof. Ms. Rosemary Toffolli www.cruzeirodosul.edu.br Campus Liberdade Rua Galvão Bueno, 868 01506-000 São Paulo SP Brasil Tel: (55 11) 3385-3000