GABRITO IBD03 Questão 1 Este algoritmo começa por escolher um elemento do vetor, chamado pivot. Normalmente o pivot é o primeiro elemento do vetor. Em cada etapa, este pivot é comparado com todos os elementos do vetor e cada elemento menor que o pivot é deslocado para o começo do vetor. Após cada passada, todos os elementos menores se encontrarão no começo do vetor, e os elementos maiores ficam no fim do vetor. O pivot é então trocado com o último elemento menor que este; suponha que esta seja a posição i do vetor. Recursivamente, passamos a ordenar da posição zero até a i, e da posição i+1 até n. A recursão avança até que a chamada tenha apenas que ordenar um elemento, que por si só, já está ordenado. No entanto, se pegarmos o pivot sempre do primeiro elemento e o vetor já estiver ordenado em ordem crescente ou decrescente, este algoritmo apresentará custo quadrático O(n 2 ). Para evitar este custo, devemos escolher o pivot de uma posição aleatória do vetor e deslocá-lo para a primeira posição. Desta forma o custo passa a ser O(nlogn), já que na maioria das vezes o valor pego como pivot vai separar o vetor, em cada passo, mais ou menos ao meio. Como exemplo, considere o vetor: 6 5 3 1 8 7 2 4 Separando o vetor segundo o pivot 6 6 5 3 1 2 4 8 7 Troque o pivot com o 4 que é o ultimo numero menor 4 5 3 1 2 6 8 7 Executa-se a separação dos subvetores 4 5 3 1 2 e 8 7, uma vez que o número 6 já está em seu lugar. Todos à esquerda são menores e todos à direita são maiores que ele. Seguindo este esquema de recursão, ao fim o vetor estará organizado em ordem crescente. Obs: No caso do vetor 8 7, como só contém dois valores, faz-se a otimização de ordená-los diretamente. O custo computacional pode ser visto da seguinte forma: considerando-se n elementos no vetor original, cada subdivisão o separa em uma potência de 2, ou seja: n n/2 + n/2 n/4 + n/4 + n/4 + n/4 n/2 k + n/2 k + (2 k vezes) + n/2 k Perceba que n/2 k = 1, o que leva a n = 2 k ; ou k = log(n). Este valor equivale à altura da árvore. Como o custo para juntar cada etapa no retorno da recursão tem custo n (o número de elementos do vetor), o que nos leva a um custo de ordenação de O(nlogn).
Questão 2 Programa em C. Os outros serão equivalentes: Nota: Não inclui os #includes necessários, pois isso o compilador pode indicar os headers necessários. void calculeprodinterno( int n, int *v, int *u, int *resp ) { for( int i = 0 ; i < n ; i++ ) resp[i] = v[i]*u[i]; int calculemodulo( n, resp ) { int sum = 0; for( int i = 0 ; i < n ; i++ ) sum += resp[i]*resp[i]; return (int)sqrt( sum ); void main( int argc, char **argv ) { int n, *v, *u, *resp; n = atoi( argv[1] ); // Pegue o tamanho dos vetores v = (int *)malloc( n*sizeof( int ) ); u = (int *)malloc( n*sizeof( int ) ); resp = (int *)malloc( n*sizeof( int ) ); for( int i = 0 ; i < n ; i++ ) { v[i] = atoi( argv[i+2] ); u[i] = atoi( argv[i+n+2] ); calculeprodinterno( n, v, u, resp ); int k = calculemodulo( n, resp ); for( int i = 0 ; i < n ; i++ ) printf( resp*%d+ = %d, i, resp*i+ ); printf( \nvalor do Modulo e' igual a %d\n, k );
Questão 3 Esta arquitetura é dividida em três partes, a saber: nível externo, nível interno e nível conceitual, sendo este último um nível de interface entre os dois primeiros. Nível Externo: Este nível é mais perto do usuário. Em termos de utilidade, este nível pode ser um usuário ou um programa de usuário ou um administrador, sendo que este último estará também interessado nos níveis conceitual e interno. Um usuário terá a sua disposição uma linguagem de programação, podendo ser uma API SQL, ou uma linguagem de alto nível como Cobol, PL1, C. Sendo a linguagem de alto nível, ressaltamos que ou ela contém um subconjunto de SQL, ou ela tem à disposição uma API que permite acessar uma sublinguagem SQL para fazer o acesso aos bancos de dados. Em princípio, toda sublinguagem SQL contém pelo menos duas partes bem distintas, Data Definition Language (DDL), que permite a declaração de objetos para os bancos de dados, e a Data Manipulation Language (DML), que permite a manipulação destes objetos. Nível Interno: Esta é uma camada de baixo nível, responsável por cuidar da gravação dos registros criados e salvos pelo nível do usuário. No entanto, esta camada da arquitetura não lida diretamente com paginação entre a memória secundária e a memória principal. Ele define o esquema interno de representação dos registros, especifica os índices necessários, como os registros gravados serão representados, a sequência física dos registros. Esta camada da arquitetura é escrita em uma data definition language própria, que não precisa ter correlação com a DDL utilizada pelo usuário no nível externo. Nível Conceitual: Esta camada intermediária faz com que os dados entrados pelo usuário sejam representados em um formato que permitirá, ao nível interno, gerenciá-los de modo adequado. Esta camada é escrita em outra data definition Language. Para garantir independência entre a representação e os dados, as definições DDL neste nível, não devem ser baseadas em nenhuma consideração a respeito das estruturas dos registros a serem gravados, nem às técnicas de acessos a esses registros. Por fim, é importante mencionar que o nível conceitual não inclui apenas a representação intermediária dos dados entre os níveis externo e interno. Esta camada é responsável por importantes tarefas, tais como, segurança e regras de integridade dos dados.
Questão 4 Exemplo de como criar uma tabela simples para guardar o nome, sobrenome e idade dos parentes: Criando um database create database familia; Abrindo o database: use familia; Criando uma tabela: create table silva ( nome varchar(20), sobrenome varchar(20), idade numeric(3), ); Ou number em sql. Averiguando a tabela: Show tables; describe silva; Inserindo os dados de dois parentes: insert into silva (nome, sobrenome, idade) values ('Geraldo', 'Silva', 22 ); insert into silva (nome, sobrenome, idade) values ('Mario', 'Silva', 26 ); Listando os dados do bando de dados família: select * from silva; select * from silva where idade > 24; Saindo do sql: quit;
Questão 5 O propósito de um sistema cliente/servidor de bancos de dados é fornecer, do lado dos aplicativos ou usuários, uma interface de alto nível de acesso aos registros de dados. De um modo simplificado, tal arquitetura pode ser formada por apenas duas camadas, de um lado um servidor (também chamado de backend ) e do outro lado por vários clientes (também chamados de frontends ). O Servidor é um Data Base Manegement System (DBMS). O DBMS é responsável por três tarefas principais: 1) Interceptar os pedidos dos usuários e analisá-los; 2) Inspecionar o esquema externo para o usuário, gerenciar o mapeamento entre as camadas externas e conceitual, gerenciar a representação conceitual, gerenciar o mapeamento entre as camadas conceitual e interna, e gerenciar as definições das estruturas de armazenamento; 3) Por fim, executar as operações necessárias sobre o banco de dados. Os Clientes são os vários aplicativos que rodam se comunicando com o DBMS, pedindo acessos aos dados. Um cliente pode ser também um usuário que, por via de comandos diretos, lança comandos de acessos ao servidor. Para o servidor, não faz diferença se o pedido vem de um aplicativo ou de um usuário direto, uma vez que ambos estarão usando o mesmo conjunto de funções de pedido de acesso aos dados. Já os aplicativos podem ser separados em duas categorias. Aplicativos implementados por usuários, normalmente escritos em C ou Cobol, ou aplicativos fornecidos por empresas, também conhecidos como ferramentas. Estas ferramentas podem se encaixar nas seguintes categorias: planilhas, pacotes estatísticos, ferramentas de gerenciamento de copias, entre outas. Se o servidor for executado em uma máquina e os clientes executarem em máquinas diferentes, essa configuração é conhecida por processamento distribuído. As diferentes máquinas são conectadas via rede. Entre as características de um sistema distribuído estão: 1) A resposta aos pedidos dos clientes já são feitos em paralelos, visto que hoje em dia, praticamente todos os computadores já são produzidos com multiprocessadores, o que aumenta a eficiência nas respostas; 2) As máquinas que executam o DBMS podem ser montadas de modo a melhorar ainda mais a performance do DBMS. 3) A máquina do cliente também pode ser personalizada de modo a oferecer melhores interfaces para query ao DBMS e facilitar sobremaneira a utilização do lado do cliente; 4) Vários clientes podem acessar um único servidor DBMS, diminuindo o custo de montagem de um sistema cliente/servidor.
Questão 6 <html> <head> <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> <meta name="description" content="3rd Workshop on Applications for Multi Core Architectures" /> <meta name="keywords" content="multicore, manycore, applications" /> <script type="text/javascript" src="mdcr.js"></script> <link rel="stylesheet" type="text/css" href="template.css" media="screen,projection" /> <link rel="stylesheet" type="text/css" href="print.css" media="print" /> <title>concurso Cepel 2014</title> </head> <body> <!-- Header --> <center> <tr><td colspan="2" class="header"> <br> <h2> <p style="font-family:times;color:#1919a0"> Ficticia S.A.<br></p> </h2> </td></tr> </center> <!-- ===================================================== --> <table border="1" width="50%" summary="this table gives some statistics about fruit flies: average height and weight, and percentage with red eyes (for both males and females)."> <caption><em>tabela Teste Para a Prova do Cepel</em></caption> <tr><th><center>nome</center><td><center>idade</center><td><center> Salário </center> <tr><th>rodrigo Maia Silva <td><center> 32 </center><td><center> 1.200,00 </center> <tr><th>regina Amaro Santos <td><center> 35 </center><td><center> 1.350,00 </center> <tr><th>laura Souza <td><center> 30 </center><td><center> 1.150,00 </center> </table> <!-- ==================================================== --> </body> </html>
Questão 7 Listas encadeadas são estruturas que permitem a alocação dinâmica de elementos que guardam dados de interesse, sendo que estes elementos contem além dos dados, um ponteiro que aponta para o próximo elemento da lista. Como exemplo mostramos abaixo uma estrutura em C que guarda um inteiro, a função principal que permite inserir elementos diferentes de zero, e ao lado a função de inserção na lista: struct _elem { int v; struct *_elem prox; elem; Void main() { elem *primeiro = NULL; int v = 1; while( v!= 0 ) { scanf( %d, &v ); if( v!= 0 ) insere( &primeiro, v ); imprime( primeiro ); Insere( elem **no, int v ) { if( *no == NULL ) { *no = malloc( sizeof(elem) ); (*no)->v = v; (*no)->prox = NULL); eles { insere( &(*no), v ) imprime( elem *no ) { if( no!= NULL ) { printf( %d, no->v ); imprime( no->prox ); Do jeito implementado, o custo de inserção será de O(n). Para que a inserção tenha custo constante, um ponteiro para o ultimo elemento da lista tem que ser mantido e atualizado a cada nova inserção. Desta forma não seria necessário percorrermos a lista toda a cada nova inserção.
Questão 8 Árvores binárias de busca são estruturas que permitem o armazenamento organizado de dados. Este tipo de árvore exige que se considere uma regra no momento da inserção de elementos. O mais usual é que cada novo elemento é comparado com cada elemento existente na árvore, e começando pela raiz, se o novo elemento for menor, será inserido à esquerda do elemento atual. Se for maior, será inserido à direita do elemento atual. Como exemplo suponha a árvore abaixo: 10 6 15 4 12 20 O elemento 10 é a raiz da arvore. Se tentarmos inserir o número 8, ele será comparado com a raiz; como é menor e existe um nó da árvore à esquerda, vamos compará-lo com esse elemento à esquerda, ou seja, o número 6. Por ser maior, e como o 6 não tem elemento à direita, o número 8 pode ser inserido neste local, resultando em: 10 6 15 4 8 12 20 O pior caso acontece na situação em que os valores são inseridos em ordem crescente ou decrescente. No caso de um conjunto de números em ordem crescente teríamos uma árvore completamente desbalanceada 4 6 8 10 12 15 20 ocasionando um custo de inserção quadrático: O(n 2 ) O melhor caso, que já foi mostrado no exemplo anterior, ocorre quando a inserção acompanha uma ordem em que não haja muita diferença de profundida entre o ramo mais alto e o mais baixo da árvore. Neste caso se diz que a árvore encontra-se balanceada.
Questão 9 Árvores rubro-negras são regidas por quatro regras que garantem seu balanceamento, ao contrário das árvores binarias de busca comuns. Essas regras consistem em se impor as seguintes restrições aos nós da árvore: 1. Todo nó é colorido de preto ou vermelho. 2. A raiz da árvore e' preta. 3. Todas as folhas da árvore são pretas. 4. Todo nó vermelho tem filhos pretos. 5. Qualquer caminho entre um nó da árvore e suas folhas descendentes tem o mesmo número de nós negros. Como exemplo, considere a árvore a seguir: A argumentação sobre o balanceamento pode ser feita de uma forma sucinta, pensando-se que, se a partir da raiz, o ramo esquerdo tem k filhos pretos, o ramo direito poderá ter no máximo 2k filhos vermelhos, o que significa que esse ramo da árvore terá uma altura de no máximo o dobro da do outro ramo. Esta diferença, por ser linear, é considerada um balanceamento em termos de custo computacional.
Questão 10 Classes são extensões dos tipos de dados abstratos structure que incluem, além da possibilidade de armazenar dados em variáveis, funções que permitem que o tratamento dos dados seja feito de forma transparente para o programador que utiliza essa classe. Essa característica traz vários benefícios. Pode se criar funções criadoras e destruturas que se encarregam de inicializar e limpar qualquer alocação de memoria, por exemplo. Além disso, as funções membro das classes, como são chamadas em programação orientada a objetos, fazem com que a manipulação dos dados de uma classe independa de o programador saber ou não o nome das variáveis. Isto permite a um grupo que mantenha um conjunto de classes, ao manter o nome das funções membro, alterar o interior das classes, sem que isso atrapalhe a reutilização de códigos e organizações externas dessas classes. Comparando com a programação procedural, na qual variáveis são criadas ao longo dos códigos, se o programador precisa mudar o nome de uma variável, todas as funções que utilizem essa variável terão que ser alteradas por todos os programadores que já a usaram.
Questão 11 O merge sort funciona em uma primeira etapa dividindo o vetor de n elementos ao meio; em seguida, ele divide cada subvetor ao meio, e assim por diante, até que reste apenas um elemento em cada subvetor. Quando chega nessa etapa, por conter apenas um elemento, este subvetor está ordenado. Então começa a segunda etapa, na qual a cada dupla vizinha de subvetores, estes são juntados de forma que, ao fim dessa fusão, os elementos estarão em ordem. Isto é feito escolhendo-se o menor elemento entre os dois subvetores; o escolhido é transportado para um vetor auxiliar e, repetindo-se o procedimento para todos os elementos dos sub-vetores, garantimos que esse subconjunto do vetor original estará ordenado. Agora repete-se o mesmo procedimento para todos os subvetores criados na primeira etapa. Ao fim, o vetor original estará ordenado. Apresentamos um exemplo a eguir, no qual representamos a separação, em cada etapa, por dois pontos: 6 5 3 1 8 7 2 4 6 5 3 1 : 8 7 2 4 6 5 : 3 1 :: 8 7 : 2 4 6 : 5 :: 3 : 1 ::: 8 : 7 :: 2 : 4 Agora que temos todos os subvetores com apenas um elemento, executamos o retorno da recursão, juntos cada dupla de subvetores em ordem crescente: 5 6 : 1 3 :: 7 8 : 2 4 1 3 5 6 : 2 4 7 8 1 2 3 4 5 6 7 8 Pronto... O custo computacional pode ser visto da seguinte forma: considerando-se n elementos no vetor original, cada subdivisão o separa em uma potência de 2, ou seja: n n/2 + n/2 n/4 + n/4 + n/4 + n/4 n/2 k + n/2 k + (2 k vezes) + n/2 k Perceba que n/2 k =1, o que leva a n = 2, ou k = log(n). Esse valor equivale à altura da árvore. Como o custo para juntar cada etapa no retorno da recursão é n (o número de elementos do vetor), segue-se que o custo de ordenação é de O(nlogn).
Questão 12 Espera-se que o candidato tenha algum conhecimento de JDBC para interfacear com um DBMS. Consideremos Oracle como o servidor. Entre os principais comandos para se iniciar uma comunicação JDBC, esperamos do candidato a menção dos seguintes: 1. Estabelecer a comunicação com o servidor: a- Carregar o driver do servidor Class.forName( oracle.jdbc.driver.oracledriver ); b- Estabelecer a comunicação em si: Connection con = DriverManager.getConnection( jdbc:oracle:thin:@servername:1544:serverinstance, username, password ); em que se espera que o candidato identifique os campos da string como: protocolo (jdbc), o vendedor (oracle), driver (thin), o servidor (servername), a porta de acesso (1544), e a instância do servidor (SERVERINSTANCE). O usuário e senha como os dados de acesso deste usuário no servidor de arquivos. 2. Criar um Statement JDBC, inserir e atulizar produtos na tabela: Statement stmt = com.createstatement(); stmt.executeupdate( CREATE TABEL Vendas + (prod varchar(20), preco real, qtdestoque real) ); stmt.executeupdate( INSERT INTO Vendas + VALUES ( 'Coca Cola', 3.5, 100 ) ); stmt.executeupdate( INSERT INTO Vendas + VALUES ( 'Cocada', 2.2, 50 ) ); 3. Ou criar um PreparedStatement JDBC: A vantagem de se usar um PreparedStatement, é que ele automatiza a atualização constante de certos dados na tabela, mas antes de usá-lo temos que indicar a chave e os valores a serem atualizados: PreparedStatement updateprice = com.preparestatement( UPDATE Vendas SET preco =? WHERE prod =? ); updateprice.setint( 1, 10 ); updateprice.setstring( 2, Coca Cola ); Indica que cada vez que rodarmos updateprice, o produto Coca Cola terá seu preço atualizado. 4. Comando SELECT Este comando retorna um conjunto de soluções que tem que se mostradas uma a uma: String prod; float preco, qtdestoque; ResultSet rs = stmt.executequery( SELECT * FROM Vendas ); while( rs.next() ) { prod = rs.getstring( prod ); preco = rs.getfloat( preco ); qtdestoque = rs.getfloat( qtdestoque ); ==========================================