Baixe o app para aproveitar ainda mais
Prévia do material em texto
Vetores e Matrizes • Já vimos em muitos programas o uso de strings: • Strings são sequências de caracteres. • Como os strings são representados no computador? • Cada caractere é representado por um código ASCII (um inteiro no intervalo [0, 255]). • Cada caractere é armazenado em 1 byte. Assim, o string é armazenado em bytes consecutivos de memória. • Para identificar o final de um string, a linguagem C utiliza um caractere especial: '\0' (código ASCII zero). © ELFS 141 printf("Numero = %d – Valor = %.2f\n",n,v); • Exemplo: O string "Linguagem C" é representado na memória como: • Esse string, portanto, consome 12 bytes de memória. • Como as posições de memória ocupadas pelo string são consecutivas, cada uma dessas posições pode ser identificada por um índice. • Assim, para armazenar um string podemos declarar uma variável indexada, também conhecida como vetor: © ELFS 142 e g L i n g u a m C \0 String char S[12]; e g L i n g u a m C \0 S[0] S[1] S[2] S[3] S[4] S[5] S[6] S[7] S[8] S[9] S[10] S[11] • Importante: Na linguagem C, os valores dos índices começam sempre em zero. • Portanto, a variável S pode ocupar até 12 posições do tipo char (ou seja, até 12 bytes). • Podemos considerar que a variável S corresponde a um conjunto de 12 variáveis do tipo char, cada uma das quais identificada por meio de um índice (de 0 a 11). • A memória alocada para uma variável é dada por: (no de posições de memória) * (tamanho do tipo, em bytes) © ELFS 143 int a; int b[15]; float c[20]; char d[100]; Var Tipo Tamanho (bytes) Nº de posições Alocada (bytes) a int 4 1 4 b int 4 15 60 c float 4 20 80 d char 1 100 100 Atenção: • Uma variável pode não ocupar todas as posições de memória alocadas. • Exemplo: char d[100] = "UNESP-FEG"; Dos 100 bytes alocados, a variável está ocupando apenas 10 (lembre-se do último caractere '\0'). • Mas, uma variável jamais poderá ocupar mais posições do que o total de posições alocadas! • Exemplo: float c[20]; x = c[0]; ok! y = c[19]; ok! z = c[20]; invasão de memória!! • A invasão de memória é um erro muito grave e irá comprometer a execução do programa. © ELFS 144 • Portanto, para usar uma variável indexada devemos saber qual deve ser o tamanho máximo da variável, ou seja, quantas posições de memória, no máximo, serão necessárias. • Para isso, pode-se definir uma constante: e declarar a variável como: • Lembrar que não há problema se for usado menos memória do que o total alocado! • Por que as variáveis indexadas são importantes? © ELFS 145 #define MAX 100 char d[MAX]; • Considere o seguinte problema: © ELFS 146 Deseja-se ler as notas obtidas pelos alunos de uma classe e determinar: a maior nota obtida, a média das notas e o número de alunos com nota abaixo da média. int i,na; float nota,MN,media; MN = -1; media = 0; printf("Quantos alunos? "); scanf("%d",&na); for (i = 0; i < na; i++) { printf("Digite uma nota: "); scanf("%f",¬a); if (nota > MN) MN = nota; media = media + nota; } media = media/na; • Determinar a maior nota e a média é relativamente fácil. • Mas como determinar o número de alunos abaixo da média? • Para determinar o número de alunos com notas abaixo da média é preciso conhecer as notas de todos os alunos! • Como? Pedindo para o usuário fornecer as notas novamente? Definindo uma variável para cada nota? Quantas variáveis deverão ser declaradas? © ELFS 147 #define MAX 100 ... float nota[MAX]; ... for (i = 0; i < na; i++) { printf("Digite uma nota: "); scanf("%f",¬a[i]); ... media = media + nota[i]; } media = media/na; abaixo = 0; for (i = 0; i < na; i++) { if (nota[i] < media) abaixo++ } ... • A solução é usar uma variável indexada! • Importante: A declaração de variáveis indexadas exige uma constante entre os colchetes [ ]. • Como evitar a invasão de memória? • Imagine que deseja-se aumentar o número de alunos da classe de 100 para 200. Que modificações precisam ser feitas no programa? © ELFS 148 ... printf("Quantos alunos? "); scanf("%d",&na); if (na > MAX) { printf("Erro: Maximo = %d",MAX); return 1; } ... Exercícios: 1. Escrever um programa que lê um vetor de N valores inteiros e mostra qual é o maior valor presente no vetor e qual é a sua posição no vetor. Deve mostrar: Maior = 9, Posicao = 4 2. Escrever um programa que, dado o valor de N (int), mostra a representação de N em binário. © ELFS 149 3 8 0 1 9 2 5 7 4 6 v[0] v[1] v[2] v[3] v[4] v[5] v[6] v[7] v[8] v[9] (62)10= (111110)2 © ELFS 150 #include <stdio.h> #include <stdlib.h> #define MAX 30 int main() { int i,N,M,P,v[MAX]; printf("Valor de N: "); scanf("%d",&N); for (i = 0; i < N; i++) { printf("Valor de v[%d]: ",i); scanf("%d",&v[i]); } M = v[0]; P = 0; for (i = 0; i < N; i++) { if (v[i] > M) { M = V[i]; P = i; } } printf("Maior = %d, Posicao = %d\n",M,P); return 0; } © ELFS 151 #include <stdio.h> #include <stdlib.h> #define MAX 20 int main() { int N,i,j,quoc,resto,db[MAX]; printf("Entre com N: "); scanf("%d",&N); quoc = N; for (i = 0; quoc >= 2; i++) { resto = quoc % 2; db[i] = resto; quoc = quoc/2; } db[i] = quoc; printf("Representacao de %d em binario: ",N); for (j = i; j >= 0; j--) printf(db[j]); printf("\n"); return 0; } Geração de números aleatórios • A linguagem C dispõe da função rand() para a geração de um inteiro aleatório no intervalo [0, RAND_MAX] (RAND_MAX é uma constante definida em stdlib.h). • Especificamente: RAND_MAX = 32767. • Para gerar números aleatórios em um intervalo [a, b] podemos escrever uma função: © ELFS 152 int aleatorio(int a, int b) { return (a + rand()%(b-a+1)); } Que valor é este? Um valor inteiro aleatório pertencente ao intervalo [0, b-a]. Somando a teremos um valor inteiro aleatório em [a, b]. © ELFS 153 #include <stdio.h> #include <stdlib.h> #define MAX 100 int aleatorio(int a, int b) { return (a + rand()%(b-a+1)); } int main() { int i,n,a,b; int V[MAX]; printf("Quantos elementos? "); scanf("%d",&n); printf("Qual intervalo [a,b]? "); scanf("%d %d",&a,&b); for (i = 0; i < n; i++) V[i] = aleatorio(a,b); printf("V = ["); for (i = 0; i < n; i++) printf(" %d",V[i]); printf("]\n"); return 0; } Ao executar o programa teremos: Quantos elementos? 10 Qual intervalo [a,b]? 5 10 V = [ 6 6 10 7 9 7 5 7 10 6] Se executarmos de novo: Quantos elementos? 10 Qual intervalo [a,b]? 5 10 V = [ 6 6 10 7 9 7 5 7 10 6] Ué? Não é aleatório? Por que gerou os mesmos elementos? Sim, os números são gerados aleatoriamente, mas a geração de números aleatórios depende de uma semente. Se a semente é a mesma, será gerada a mesma sequência de números aleatórios. • A geração da mesma sequência de números aleatórios é interessante durante a depuração do programa (executar o programa com os mesmos dados para encontrar os erros). • Quando se deseja diferentes sequências de números aleatórios, devemos alterar a semente. • Para garantir que a sequência gerada seja sempre diferente, deve-se mudar a semente a cada execução. Como? • Uma boa idéia é usar o valor retornado pela função time() (definida em time.h). • A função time(NULL) retorna o número de segundos transcorridos desde 01/01/1970. Portanto, o valor retornado será diferente a cada execução. © ELFS 154 • A função srand() é responsável pela inicialização da semente a ser usada pela função rand(). • A função srand() exige como parâmetro um valor inteiro do tipo unsigned. • Podemos então escrever: • A função srand() deve ser executada apenas 1 vez no programa, pois basta alterar o valor da semente inicial. © ELFS 155 srand((unsigned)time(NULL));#include <stdio.h> #include <stdlib.h> #include <time.h> int main() { srand((unsigned)time(NULL)); ... } Quantos elementos? 10 Qual intervalo [a,b]? 5 10 V = [ 7 5 8 10 10 5 9 9 6 9] Nova execução: Quantos elementos? 10 Qual intervalo [a,b]? 5 10 V = [ 7 5 10 8 8 5 5 6 9 8] • Exercício: Uma loja pretende simular um dia de suas vendas. Os preços dos produtos desta loja estão armazenados no vetor preco[15] e considera-se que cada cliente que aparece na loja compra apenas um produto. Os produtos comprados pelos clientes devem ser gerados aleatoriamente. Imagina-se que a loja receba de 1000 a 2000 clientes por dia (gerar aleatoriamente). O vetor compra[] armazena os produtos comprados pelos clientes. compra O cliente 0 comprou o produto 8 O cliente 1 comprou o produto 5 O cliente 2 comprou o produto 10 O cliente 3 comprou o produto 14 ... © ELFS 156 8 5 10 14 7 0 1 2 3 4 1999 ... ... • O resultado da simulação deve mostrar: • O número de clientes no dia; • O valor total das vendas no dia; • O produto mais vendido e seu preço; • O produto menos vendido e seu preço; • O valor médio das vendas. • Para determinar o produto mais vendido e o produto menos vendido temos que armazenar quantas vezes cada produto foi vendido. Vamos usar o vetor venda[15]: venda • Neste caso, o produto 0 foi vendido 2 vezes, o produto 1 foi vendido 5 vezes, o produto 2 não foi vendido, ... © ELFS 157 2 5 0 7 0 0 1 2 3 4 14 ... ... © ELFS 158 #include <stdio.h> #include <stdlib.h> #include <time.h> #define MAX 2000 int aleatorio(int a, int b) { return (a + rand()%(b-a+1)); } int main() { int i,n,p; int vmais,vmenos,pmais = 0,pmenos = 0; float total = 0,media; int compra[MAX],venda[15]; float preco[] = {72,15,25,32,98,45,22,80,63,45,15,21,95,40,50}; float venda[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; srand((unsigned)time(NULL)); n = aleatorio(1000,2000); for (i = 0; i < n; i++) { p = aleatorio(0,14); compra[i] = p; venda[p]++; } Notar como pode ser feita a inicialização de um vetor. Neste caso, não é preciso especificar o tamanho do vetor entre os colchetes []. O tamanho fica definido pela quantidade de valores enumerados entre as chaves { }. © ELFS 159 vmais = -1; vmenos = n+1; for (i = 0; i < n; i++) { p = compra[i]; total = total + preco[p]; if (venda[p] > vmais) { vmais = venda[p]; pmais = p; } if (venda[p] < vmenos) { vmenos = venda[p]; pmenos = p; } } media = total/n; printf("Numero de clientes: %d\n",n); printf("Valor total das vendas: %.2f\n",total); printf("Mais vendido: %d - Preco = %.2f\n",pmais,preco[pmais]); printf("Menos vendido: %d - Preco = %.2f\n",pmenos,preco[pmenos]); printf("Valor medio das vendas: %.2f\n",media); return 0; } Uma possível execução: Numero de clientes: 1161 Valor total das vendas: 55721.00 Mais vendido: 2 - Preco = 25.00 Menos vendido: 8 - Preco = 63.00 Valor medio das vendas: 47.99 • Observar que o índice de uma variável indexada deve ser um valor inteiro (limitado ao número de posições de memória declaradas para a variável). • O que importa é o índice ser um inteiro, cujo valor não exceda o tamanho do vetor. © ELFS 160 Este valor inteiro pode ser: Exemplo uma constante venda[12] = ... o valor de uma variável venda[k] = ... o valor de uma expressão venda[k+1] = ... o valor retornado por uma função venda[aleatorio(0,14)] = ... o valor de uma outra variável indexada venda[compra[i]] = ... No programa, em vez de: p = compra[i]; total = total + preco[p]; poderíamos escrever: total = total + preco[compra[i]]; Ordenação de Vetores • A ordenação de dados é uma operação importante em computação. Muitos problemas podem ser resolvidos eficientemente se os dados estiverem ordenados. • Existem vários algoritmos de ordenação. • O algoritmo que veremos a seguir é um dos mais simples e menos eficientes: ordenação por contagem. • Exemplo. Imagine um vetor contendo os seguintes valores: • A ideia da ordenação por contagem é determinar, para cada elemento x, quantos elementos menores do que x existem no vetor. © ELFS 161 v 38 97 19 62 23 0 1 2 3 4 8 7 47 5 41 6 • Por exemplo: Quantos elementos menores do que 38 existem no vetor v? • A ideia é usar um outro vetor (de mesmo tamanho) para armazenar os dados ordenados. • Se existem 3 elementos menores que 38, qual é a posição de 38 no vetor ordenado w? © ELFS 162 v 38 97 19 62 23 0 1 2 3 4 8 7 47 5 41 6 w 0 1 2 3 4 7 5 6 w 0 1 2 3 4 7 5 6 38 Resposta: p = 3 © ELFS 163 #include <stdio.h> #include <stdlib.h> #include <time.h> #define MAX 100 int aleatorio(int a, int b) { return (a + rand()%(b-a+1)); } int main() { int i,j,n,p,v[MAX],w[MAX]; srand((unsigned)time(NULL)); printf("Quantos elementos? "); scanf("%d",&n); if (n > MAX) { printf("Max = %d\n",MAX); return 0; } for (i = 0; i < n; i++) { v[i] = aleatorio(1,99); } © ELFS 164 printf("Vetor gerado: ["); for (i = 0; i < n; i++) printf(" %d",v[i]); printf("]\n"); // Ordenacao por contagem for (i = 0; i < n; i++) { p = 0; for (j = 0; j < n; j++) { if (v[j] < v[i]) { p++; } } w[p] = v[i]; } printf("Vetor ordenado: ["); for (i = 0; i < n; i++) printf(" %d",w[i]); printf("]\n"); return 0; } © ELFS 165 printf("Vetor gerado: ["); for (i = 0; i < n; i++) printf(" %d",v[i]); printf("]\n"); // Ordenacao por contagem for (i = 0; i < n; i++) { p = 0; for (j = 0; j < n; j++) { if (v[j] < v[i]) { p++; } } w[p] = v[i]; } printf("Vetor ordenado: ["); for (i = 0; i < n; i++) printf(" %d",w[i]); printf("]\n"); return 0; } Reparem nos trechos destacados que se trata da mesma sequência de instruções, mas com dados diferentes. Podemos construir uma função que recebe os dados como parâmetros e executa essa sequência de instruções. void mostra(int t, int a[], char m[]) { int i; printf("%s: [",m); for (i = 0; i < t; i++) printf(" %d",a[i]); printf("]\n"); } © ELFS 166 mostra(n,v,"Vetor gerado"); // Ordenacao por contagem for (i = 0; i < n; i++) { p = 0; for (j = 0; j < n; j++) { if (v[j] < v[i]) { p++; } } w[p] = v[i]; } mostra(n,w,"Vetor ordenado"); return 0; } void mostra(int t, int a[], char m[]) { int i; printf("%s: [",m); for (i = 0; i < t; i++) printf(" %d",a[i]); printf("]\n"); } • Usando a função mostra: • Vamos observar a função mostra: • Numa lista de parâmetros de função, um vetor é declarado sempre sem especificar o tamanho: int a[]. Isto porque a função deve funcionar qualquer que seja o tamanho. • Mas a função precisa saber qual é o tamanho do vetor, que também deve ser declarado como um parâmetro (int t). • Notar que char m[] indica que m é um string (vetor de caracteres). Por que para m não precisa declarar o tamanho? © ELFS 167 void mostra(int t, int a[], char m[]) { int i; printf("%s: [",m); for (i = 0; i < t; i++) printf(" %d",a[i]); printf("]\n"); } Por exemplo: © ELFS 168 // Ordenacao por contagem for (i = 0; i < n; i++) { p = 0; for (j = 0; j < n; j++) { if (v[j] < v[i]) { p++; } } w[p] = v[i]; } v 38 97 19 38 23 0 1 2 3 4 19 5 w 0 1 2 3 4 5 Este algoritmo tem um problema: o que acontece se existirem valores duplicados no vetor? 38 97 19 23 Exercício: Corrigir o algoritmo para que o vetor ordenado contenha os mesmos valores do vetor original. • Uma outra operação importante na computação éverificar se um vetor contém um determinado valor. • Imagine uma função procura que retorna 1 se um vetor contém um determinado valor e retorna 0, caso contrário. Como programar a função procura? Quais devem ser os parâmetros da função? • A função procura pode ser usada para gerar vetores que não contenham valores duplicados. Como? © ELFS 169 int procura(int t, int a[], int v) { int i; for (i = 0; i < t; i++) { if (a[i] == v) return 1; } return 0; } © ELFS 170 int main() { int v[MAX]; int i,n,x; srand((unsigned)time(NULL)); printf("Quantos elementos? "); scanf("%d",&n); i = 0; while (i < n) { x = aleatorio(1,99); if (procura(i,v,x) == 0) { v[i] = x; i++; } } mostra(n,v,"Vetor gerado"); return 0; } © ELFS 171 Matrizes • Uma variável indexada pode ter uma ou mais dimensões. Se tem somente uma dimensão é chamada de vetor. Se tem mais de uma dimensão, é chamada de matriz. • Exemplo: Neste caso, a variável a é um vetor, b é uma matriz bidimensional e c é uma matriz tridimensional. int a[10]; char b[3][5]; float c[2][2][3]; Variável Tamanho Memória alocada (bytes) Exemplo de uso a 10 10 * 4 = 40 a[i] = 0 b 3*5 = 15 15 * 1 = 15 b[i][j] = 0 c 2*2*3 = 12 12 * 4 = 48 c[i][j][k] = 0 © ELFS 172 • Neste curso, vamos considerar apenas matrizes bidimensionais. • Para melhor compreensão, utiliza-se uma abstração sobre a disposição espacial dos elementos de uma matriz. • Exemplo: b[3][5] • Exercício. Atribuir os valores destacados às variáveis indexadas correspondentes. 6 b b[1][3] = 6; b[2][1] = 4; Imagina-se que os 15 elementos da matriz estejam dispostos em 3 linhas e 5 colunas. Isso é apenas uma abstração! 4 © ELFS 173 • A disposição espacial dos elementos de uma matriz é apenas uma abstração. • Exemplo: b[3][5] • Na memória do computador estes valores estão armazenados em posições consecutivas: • Como o computador consegue determinar os endereços de memória de cada elemento da matriz? endereço(b[i][j]) = endereço(b) + NC*(i) + j; onde NC é o número de colunas da matriz. 8 1 2 4 6 3 5 2 7 1 2 8 1 4 3 8 1 2 4 6 3 5 2 7 1 2 8 1 4 3 • Exercício. A produção anual de uma fábrica é registrada, mês a mês, em uma matriz P (12 x N). Por exemplo: • Considere que o lucro obtido com a venda de cada unidade dos produtos está armazenado no vetor L. © ELFS 174 185 240 93 A fábrica produziu 185 unidades do produto 2 no mês 0, produziu 240 unidades do produto 0 no mês 5 e produziu 93 unidades no mês 8. Fazer um programa que, dados N e o vetor L, calcula o lucro anual da fábrica por produto e o lucro anual total da fábrica. A matriz P deve ser gerada aleatoriamente, com valores no intervalo [50, 250]. 25 81 47 Cada unidade do produto 0 gera um lucro de 25, e assim por diante. © ELFS 175 #include <stdio.h> #include <stdlib.h> #include <time.h> #define MAX 50 int aleatorio(int a, int b) { return (a + rand()%(b-a+1)); } int main() { int P[12][MAX],L[MAX]; int i,j,n,custo,venda,lucro,tp,total; srand((unsigned)time(NULL)); printf("Quantos produtos? "); scanf("%d",&n); printf("Lucro dos produtos: "); for (i = 0; i < n; i++) { scanf("%d",&L[i]); } © ELFS 176 for (i = 0; i < 12; i++) { for (j = 0; j < n; j++) { P[i][j] = aleatorio(50,250); } } • Vamos imaginar as seguintes funções: • totalProduzido: retorna a quantidade total produzida de um determinado produto. • lucroPorProduto: retorna o lucro total obtido para um determinado produto. • Quais devem ser os parâmetros dessas funções? • totalProduzido: índice do produto, matriz de produção. • lucroPorProduto: índice do produto, vetor de lucros e total produzido do produto. © ELFS 177 int totalProduzido(int j, int P[][MAX]) { int i,tp; tp = 0; for (i = 0; i < 12; i++) tp = tp + P[i][j]; return tp; } int lucroPorProduto(int j, int L[], int tp) { int lucro; lucro = L[j]*tp; return lucro; } A função totalProduzido precisa saber qual é o produto (j) e qual é a matriz de produção (P). A função lucroPorProduto precisa saber qual é o produto (j), qual é o vetor de lucros (L) e qual é total produzido deste produto (tp). • Observar que para matrizes é preciso informar o número de colunas na lista de parâmetros. Por que? © ELFS 178 • Com essas funções, o trecho final do programa será: • Observar que, na chamada das funções, o parâmetro passado é apenas o nome da matriz (ou do vetor). total = 0; for (j = 0; j < n; j++) { tp = totalProduzido(j,P); printf("Produto %d: Total produzido = %d\n",j,tp); lucro = lucroPorProduto(j,L,tp); printf("Produto %d: Lucro = %d\n",j,lucro); total = total + lucro; } printf("Lucro Total = %d\n",total); return 0; } © ELFS 179 Exercício. Um quadrado mágico é uma matriz n x n cujos elementos são números inteiros e a soma dos elementos de qualquer linha ou de qualquer coluna resulta sempre no mesmo valor. Por exemplo, para n = 3: Escrever uma função quadradoMagico que recebe como parâmetros, um inteiro n e uma matriz M (n × n) e retorna 1 se M é um quadrado mágico, ou retorna 0, caso contrário. Escrever as funções somaLinha e somaColuna. Quais devem ser os parâmetros dessas funções? Escrever um programa que, dados n e a matriz M, mostra se M é ou não um quadrado mágico. 2 7 6 9 5 1 4 3 8 © ELFS 180 int somaLinha(int n, int M[][MAX], int i) { int j,soma; soma = 0; for (j = 0; j < n; j++) soma = soma + M[i][j]; return soma; } int somaColuna(int n, int M[][MAX], int j) { int i,soma; soma = 0; for (i = 0; i < n; i++) soma = soma + M[i][j]; return soma; } © ELFS 181 int quadradoMagico(int n, int M[][MAX]) { int i,j,LM,CM,soma; soma = somaLinha(n,M,0); for (i = 1; i < n; i++) { if (soma != somaLinha(n,M,i)) { return 0; } } for (j = 0; j < n; j++) { if (soma != somaColuna(n,M,j)) { return 0; } } return 1; } © ELFS 182 int main() { int M[MAX][MAX]; int i,j,n,x; printf("Tamanho: "); scanf("%d",&n); for (i = 0; i < n; i++) for (j = 0; j < n; j++) scanf("%d",&M[i][j]); x = quadradoMagico(n,M); if (x == 1) { printf("A matriz e' um quadrado magico\n"); } else { printf("A matriz nao e' um quadrado magico\n"); } return 0; } © ELFS 183 • Exercício. Simular o lançamento de dois dados. Qual é a combinação mais provável e qual é a soma mais provável dos dois dados após 50000 lançamentos? Imagine que uma matriz 6 x 6 é usada para armazenar o número de ocorrências de cada combinação de valores dos dois dados. Considere que um vetor armazena a frequência de cada valor possível da soma dos dois dados. 41 428 0 1 2 3 4 5 6 7 8 9 10 11 12 d s Significa que d1 = 3 e d2 = 4 ocorreu 41 vezes. Significa que soma = 9 ocorreu 428 vezes.
Compartilhar