• Nenhum resultado encontrado

CIC 110 Análise e Projeto de Algoritmos I

N/A
N/A
Protected

Academic year: 2022

Share "CIC 110 Análise e Projeto de Algoritmos I"

Copied!
42
0
0

Texto

(1)

CIC 110

Análise e Projeto de Análise e Projeto de

Algoritmos I Algoritmos I

Universidade Federal de Itajubá

Prof. Roberto Affonso da Costa Junior

(2)

AULA 02 AULA 02

– C++ e bibliotecas

– Vector

(3)

C++ C++

Os programas que iremos usar vai ser em C++ e ter o seguinte cabeçalho e corpo:

#include <bits/stdc++.h>

using namespace std;

int main() {

// Aqui você coloca a solução do problema return 0;

(4)

Ordem do programa Ordem do programa

Qual a ordem do programa a seguir?

Qual o tempo para n = 100? n = 1000?

#include <bits/stdc++.h>

using namespace std;

int main() { int n;

scanf("%d", &n);

for (int i = 1; i <= n; i++) { printf("%d\n", i);

}return 0;

}

(5)

Arquivo sh Arquivo sh

Salve o programa anterior com aula.cpp

Escreva o seguinte programa e salve como aula.sh

#!/bin/bash g++ aula.cpp

time ./a.out <entrada.txt >s g++ -O1 aula.cpp

time ./a.out <entrada.txt >s g++ -O2 aula.cpp

time ./a.out <entrada.txt >s g++ -O3 aula.cpp

(6)

Tempo com n = 100 Tempo com n = 100

Vá ao terminal e rode o ./aula.sh O que significa os tempos:

real 0m0.010s user 0m0.002s sys 0m0.004s real 0m0.007s user 0m0.003s sys 0m0.003s real 0m0.006s

user 0m0.001s sys 0m0.005s real 0m0.011s user 0m0.002s sys 0m0.005s

(7)

Tempo com n = 1000 Tempo com n = 1000

E agora?

Como fica o tempo?

Será que tem diferença com

n = 10000?

(8)

Programa 2 Programa 2

Repita o procedimento para o programa.

#include <bits/stdc++.h>

using namespace std;

int main() {

int n;

scanf("%d", &n);

for (int i = 1; i <= n; i++) {

for (int j = 1; j <= n; j++) {

printf("%d\n", i);

} }

return 0;

}

(9)

Ordem da magnitude Ordem da magnitude

Repita o procedimento para os programas.

#include <bits/stdc++.h>

using namespace std;

int main() {

int n;

scanf("%d", &n);

for (int i = 1; i <= 3*n; i++) {

printf("%d\n", i);

}

#include <bits/stdc++.h>

using namespace std;

int main() {

int n;

scanf("%d", &n);

for (int i = 1; i <= n + 5; i++) {

printf("%d\n", i);

}

(10)

Ordem da magnitude Ordem da magnitude

Repita o procedimento para os programas.

#include <bits/stdc++.h>

using namespace std;

int main() {

int n;

scanf("%d", &n);

for (int i = 1; i <= n; i+=2) {

printf("%d\n", i);

}

return 0;

}

(11)

Fase Fase

Repita o procedimento para o programa.

#include <bits/stdc++.h>

using namespace std;

int main() {

int n;

scanf("%d", &n);

for (int i = 1; i <= n; i+=2) {

printf("%d\n", i);

}

for (int i = 1; i <= n; i++) {

for (int j = 1; j <= n; j++) {

printf("%d\n", i);

} }

for (int i = 1; i <= 3*n; i++) {

printf("%d\n", i);

}

(12)

Varias variáveis Varias variáveis

Repita o procedimento para o programa.

#include <bits/stdc++.h>

using namespace std;

int main() {

int n, m;

scanf("%d %d", &n, &m);

for (int i = 1; i <= n; i++) {

for (int j = 1; j <= m; j++) {

printf("%d\n", i);

} }

return 0;

}

(13)

Recursão Recursão

Repita o procedimento para o programa.

#include <bits/stdc++.h>

using namespace std;

int f(int n) {

if (n == 1) return 1;

f(n-1);

}

int main() {

int n;

scanf("%d", &n);

printf("%d\n", f(n));

(14)

Recursão Recursão

Repare que a função f do programa anterior é de ordem O(1). Contudo, a função recursiva chama o programa n vezes, tendo um tempo total de O(n).

(15)

Recursão Recursão

Repita o procedimento para o programa.

#include <bits/stdc++.h>

using namespace std;

int g(int n) {

if (n == 1) return 1;

g(n-1);

g(n-1);

}

int main() {

int n;

scanf("%d", &n);

printf("%d\n", g(n));

(16)

Recursão Recursão

Neste caso, cada chamada de função gera duas outras chamadas, exceto n = 1. Vamos ver o que acontece quando g é chamado com o parâmetro n. A tabela a seguir mostra as chamadas de função produzidas por esta única chamada:

Com base nisso, a complexidade do tempo é

função chamada número de chamadas

g(n) 1

g(n-1) 2

g(n-2) 4

g(1) 2n-1

1+2+4+⋯+2n1=2n1=O(2n)

(17)

paper paper

Estude o assunto anterior, os programas e escreva um paper que explique os resultados. Não esqueça de que o paper não pode ter copia de texto. Deve ter no mínimo 2 ou 3 páginas.

Procure o professor se for o caso.

(18)

Vector Vector

A classe vector é uma alternativa à representação de array primitivo.

vector<int> v;

Alguns métodos:

v.size(); // retorna tamanho do vetor v

v.empty(); // determina se vetor v está vazio

vx.resize(novo_tamanho); // redimensiona vetor v

v2 = v; // cópia v em v2

(19)

Vector Vector

A classe vector é uma alternativa à representação de array primitivo.

vector<int> v;

Alguns métodos:

v.size(); // retorna tamanho do vetor v

v.empty(); // determina se vetor v está vazio

vx.resize(novo_tamanho); // redimensiona vetor v

v2 = v; // cópia v em v2

v.push_back(x); // inserir elemento no vector v.pop_back(x); // retira o ultimo elemento do vector

(20)

Iterator Iterator

É o mecanismo usado para "andar", elemento por elemento, por uma coleção de dados. É uma forma abstrata e genérica de tratar o avanço entre os elementos dessa coleção. Esse avanço pode se dar de várias formas, inclusive ao contrário.

O funcionamento exato depende de cada tipo de dado, o importante é que se um tipo possui um iterador em conformidade com a linguagem toda operação que iteração poderá ser feita com aquele objeto. Não importa para ele a complexidade da operação, nem como ela deverá ser feita. É uma forma independente da implementação de acessar os dados da coleção.

Ele possui os métodos begin() e end() pra indicar onde começa e onde termina a iteração.

(21)

Iterator Iterator

#include <bits/stdc++.h>

using namespace std;

int main() {

int n, x;

vector <int> v;

vector <int>:: iterator it;

scanf("%d", &n);

for (int i = 0; i < n; i++) {

scanf("%d", &x);

v.push_back(x);

}

for (it = v.begin(); it != v.end(); ++it) {

printf("%d ", *it);

}

printf("\n");

(22)

Problema Problema

Dado um vetor com números inteiros positivos e negativos, encontre a sublista contígua de maior soma a partir de uma lista de números. Por exemplo, dado o vetor V a seguir:

A sublista contígua de maior soma é:

V 10 5 -17 2020 50 -1 3 -30 10

V 10 5 -17 2020 50 -1 3 -30 10

(23)

Histórico Histórico

O problema de encontrar a sublista contígua de maior soma a partir de uma lista de números teve origem numa versão bidimensional mais complexa de um problema de emparelhamento de padrões inicialmente apresentado por Ulf Grenander, da Brown University.

Ao perceber que o algoritmo cúbico era impraticável para a resolução da versão unidimensional mais simples do problema de identificação de sequências contínuas de maior soma, Grenander desenvolveu a versão quadrática do algoritmo. Posteriormente, Michael Shamos, da atual Carnegie-Mellon

(24)

Histórico Histórico

Por fim, o estatístico J. (Jay) B. Kadane, da mesma Universidade de Shamos, desenvolveu dias depois a versão linear do algoritmo para o problema da sublista contígua de maior soma. Esse algoritmo permanece até os dias atuais como a versão mais eficiente e a melhor solução possível, pois qualquer algoritmo que pretenda resolver o problema em estudo deve necessariamente percorrer os N elementos da lista dada como entrada.

(25)

Kadane Kadane

O algoritmo é chamado de “Algoritmo de Kadane”.

(26)

Solução 1 Solução 1

Uma maneira direta de resolver o problema é passar por todos os subarray possíveis, calcular a soma dos valores em cada subarray e manter a soma máxima.

No programa a seguir, as variáveis i e j variam o primeiro e último índice do subarray e a soma dos valores é calculada para a variável “soma”. A variável melhor contém a soma máxima encontrada durante a pesquisa.

A complexidade do tempo do algoritmo é O(n3), porque consiste em três loops aninhados.

(27)

Solução de ordem 3 Solução de ordem 3

#include <bits/stdc++.h>

using namespace std;

int main() {

int n, soma_maior = 0, x;

vector <int> v;

scanf("%d", &n);

for (int i = 0; i < n; i++) {

scanf("%d", &x);

v.push_back(x);

}

for (int i = 0; i < n; i++) {

for (int j = i; j < n; j++)

(28)

Solução de ordem 3 Solução de ordem 3

int soma = 0;

for (int k = i; k <= j; k++) {

soma += v[k];

}

soma_maior = max(soma_maior, soma);

} }

printf("A soma maior da sublista eh: %d\n", soma_maior);

return 0;

}

(29)

Solução 2 Solução 2

É fácil tornar o Algoritmo anterior mais eficiente, basta remover um loop dele. Isto é possível calculando a soma da extremidade a direta conforme o subarray vai sendo movido.

Após essa mudança, a complexidade do tempo é O(n2).

(30)

Solução de ordem 2 Solução de ordem 2

#include <bits/stdc++.h>

using namespace std;

int main() {

int n, soma_maior = 0, x;

vector <int> v;

scanf("%d", &n);

for (int i = 0; i < n; i++) {

scanf("%d", &x);

v.push_back(x);

}

(31)

Solução de ordem 2 Solução de ordem 2

for (int i = 0; i < n; i++) {

int soma = 0;

for (int j = i; j < n; j++) {

soma += v[j];

soma_maior = max(soma_maior, soma);

} }

printf("A soma maior da sublista eh: %d\n", soma_maior);

return 0;

}

(32)

Solução 3 Solução 3

Outra estratégia é a de “Divisão e Conquista”

“Para resolver um problema de tamanho N, resolva recursivamente 'b' problemas de tamanho aproximado N/b e combine suas soluções para obter a resposta da versão completa do problema.”

Inicialmente, a lista de tamanho N é divida em dois subproblemas de tamanho aproximado N/2, os quais serão chamados, respectivamente, de A e B.

Depois, recursivamente o algoritmo vai encontrar a maior sublista contígua em A (max_A) e a maior sublista contígua em B (max_B).

(33)

Solução 3 Solução 3

No entanto, a maior sublista contígua também pode estar parte em A e parte em B, o que será computado pela variável max_C. Para a computação de max_C, é utilizado o fato de que sua maior parte em A deve começar exatamente na extremidade direita de A (a qual faz fronteira com B) e avançar para dentro da parte A e da mesma forma a maior parte em B deve começar na extremidade de B que faz fronteira com A e avançar para dentro da parte B. Assim, max_C corresponderá à soma dessas duas maiores partes definidas anteriormente. sucessivas divisões por 2 do problema inicial e dos subproblemas gerados a cada

(34)

Solução 3 Solução 3

No caso base do algoritmo, é tratado o caso da menor sublista possível, a qual corresponde à de apenas um elemento e nesse caso a maior soma é o próprio elemento ou zero se o mesmo for negativo. Ainda no caso base, também é dado o tratamento para o caso da lista vazia, ou seja, com nenhum elemento. Nesse caso, a saída é definida como zero.

(35)

Solução 3 Solução 3

O tempo de execução do algoritmo de divisão e conquista é O(n log n), significativamente mais rápido que o algoritmo quadrático anteriormente apresentado. Esse tempo de execução advém do fato de que o algoritmo faz O(n) operações a cada nível das chamadas recursivas e o número de níveis corresponde a O(log n), a altura de uma árvore binária completa resultante das sucessivas divisões por 2 do problema inicial e dos subproblemas gerados a cada chamada recursiva.

(36)

Solução de ordem n log n Solução de ordem n log n

#include <bits/stdc++.h>

using namespace std;

int divisao_conquista(vector <int> v, int a, int b) {

int c, max_A, max_B, max_C, soma;

if (a == b) {

return max(0, v[a]);

}

c = (a + b) / 2;

max_A = divisao_conquista(v, a, c);

max_B = divisao_conquista(v, c + 1, b);

max_A = soma = v[c];

for (int i = c - 1; i >= a; --i) {

soma += v[i];

max_A = max(max_A, soma);

}

(37)

Solução de ordem n log n Solução de ordem n log n

max_B = soma = v[c + 1];

for (int i = c + 2; i <= b; ++i) {

soma += v[i];

max_B = max(max_B, soma);

}

max_C = max_A + max_B;

max_C = max(max_A, max_C);

return max(max_C, max_B);

}

(38)

Solução de ordem n log n Solução de ordem n log n

int main() {

int n, soma_maior, x;

vector <int> v;

scanf("%d", &n);

for (int i = 0; i < n; i++) {

scanf("%d", &x);

v.push_back(x);

}

soma_maior = divisao_conquista(v, 0, n);

printf("A soma maior da sublista eh: %d\n", soma_maior);

return 0;

}

(39)

Solução 4 Solução 4

Surpreendentemente, é possível resolver o problema em O(n), o que significa que apenas um loop é suficiente. A ideia é calcular, para cada posição da matriz, a soma máxima de um subarray que termina nessa posição. Depois disso, a resposta para o problema é o máximo dessas somas.

Considere o subproblema de encontrar o subarray da soma máxima que termina na posição i. Existem duas possibilidades:

1.O subarray apenas contém o elemento na posição i.

(40)

Solução 4 Solução 4

No último caso, uma vez que queremos encontrar um subarray com soma máxima, o subarray que termina na posição i - 1 também deve ter a soma máxima.

Assim, podemos resolver o problema de forma eficiente ao calcular a soma de subarray máxima para cada posição final da esquerda para a direita.

O algoritmo contém apenas um loop, de modo que a complexidade do tempo é O(n). Esta é também a melhor complexidade de tempo possível, porque qualquer algoritmo para o problema tem que examinar todos os elementos da matriz pelo menos uma vez.

(41)

Solução de ordem 1 Solução de ordem 1

#include <bits/stdc++.h>

using namespace std;

int main() {

int n, soma_maior, soma, x;

vector <int> v;

scanf("%d", &n);

for (int i = 0; i < n; i++) {

scanf("%d", &x);

v.push_back(x);

}

soma_maior = soma = 0;

for (int i = 0; i < n; i++) {

soma = max(v[i], soma + v[i]);

soma_maior = max(soma_maior, soma);

}

printf("A soma maior da sublista eh: %d\n", soma_maior);

(42)

Exercício Exercício

Timus 1146 UVA 108 UVA 11059 UVA 10827 UVA 507 UVA 10154 UVA 12640

Codeforces 327 A Codeforces 279 B URI 1310

URI 1932

SPOJ BAPOSTAS SPOJ SALDO

Você pode fazer eles no codepit.io

CIC 110 2 senha: unifei

Você pode fazer eles no codepit.io

CIC 110 2 senha: unifei

Referências

Documentos relacionados

Local de realização da avaliação: Centro de Aperfeiçoamento dos Profissionais da Educação - EAPE , endereço : SGAS 907 - Brasília/DF. Estamos à disposição

Promovido pelo Sindifisco Nacio- nal em parceria com o Mosap (Mo- vimento Nacional de Aposentados e Pensionistas), o Encontro ocorreu no dia 20 de março, data em que também

Segundo o mesmo autor, a animação sociocultural, na faixa etária dos adultos, apresenta linhas de intervenção que não se esgotam no tempo livre, devendo-se estender,

- Se o estagiário, ou alguém com contacto direto, tiver sintomas sugestivos de infeção respiratória (febre, tosse, expetoração e/ou falta de ar) NÃO DEVE frequentar

Capitalismo Sindical” e consiste basicamente em dizer que processos políticos podem ter o seu princípio alterado em detrimento de uma outra classe social. No caso das Primaveras

Tabela 3 - Duração do ciclo após a germinação, produtividade, eficiência no uso da água em cenários climáticos futuros RCP4.5 para o milho semeado em 10 de outubro.. Já para

Segundo Brown (2004), apoiado principalmente no trabalho de Warren e Nisbet (1999), é possível conceber que os indivíduos, quer sejam professores ou estudantes, geralmente

Como prenuncia o próprio título do texto, O pensamento cria, embora fique evidente que aí se trata da relação entre música e artes visuais, não deixamos de notar que o foco