Laboratório de Programação Lição n.º 3 Somas acumuladas
Somas acumuladas Soma de arrays. Acumulação linear. Soma de matrizes. Acumulação bidimensional. Técnicas de acumulação. 4/4/16 Programação Imperativa 2
Soma de arrays Uma das primeiras funções sobre arrays que estudámos foi que calcula a soma de todos os elementos de um array. Recordando, ei-la, para um array de ints a, com n elementos: int ints_sum(const int *a, int n) int result = 0; for (int i = 0; i < n; i++) result += a[i]; return result; 4/4/16 Programação Imperativa 3
Mais exatamente... A função ints_sum soma os n primeiros elementos de um array a que tem pelo menos n elementos. Em rigor, a função não sabe quantos elementos o array tem; ela apenas recebe ordem de somar os n primeiros elementos, presumindo que o tamanho do array (medido em número de elementos) é maior ou igual a n. 4/4/16 Programação Imperativa 4
Subarrays A função ints_sum também serve para somar subarrays. Por exemplo, para somar os n elementos do array a, começando na posição d, invoca-se a função assim: Dizemos que d representa o deslocamento do subarray, em relação ao array a.... ints_sum(a+d, n)... Neste caso, presume-se que o tamanho do array a é pelo menos d+n. 4/4/16 Programação Imperativa 5
Somas acumuladas Um exercício clássico de programação com arrays consiste em, dado um array a com n elementos, construir um array b tal que b[i] é a soma dos i primeiros elementos de a: int ints_accumulate(const int *a, int n, int *b) b[0] = 0; for (int i = 0; i < n; i++) b[i+1] = a[i] + b[i]; return n+1; Seria disparate programar assim: int ints_accumulate(const for (int i = 0; i <= n; i++) // <= b[i] = ints_sum(a, i); return n+1; int *a, int n, int *b) não porque estivesse errado, mas porque obrigaríamos o computador a trabalhar muito mais do que é necessário. 4/4/16 Programação Imperativa 6
Somas de subarrays via acumulação Em situações em que seja preciso calcular muitas vezes a soma de subarrays de um dado array, é mais prático pré-calcular o respetivo array de acumulação e obter a soma através da diferença de dois valores acumulados, em vez de calcular iterativamente: int ints_sub_sum_via_accumulation(int *acc, int d, int n) return acc[d+n] - acc[d]; Aliás, em casos simples, será mais prático escrever logo a diferença, sem recorrer à função. 4/4/16 Programação Imperativa 7
Teste unitário da acumulação Eis uma função de teste unitário, para validar em conjunto a acumulação e a soma de subarrays via acumulação: void unit_test_accumulation(void) int n = 6; int a[6] = 3, 7, 2, 8, 5, 1; int acc[7]; int m = ints_accumulate(a, n, acc); assert(m == n+1); for (int d = 0; d < n+1; d++) for (int size = 0; d + size < n+1; size++) assert(ints_sum(a+d, size) == ints_sub_sum_via_accumulation(acc, d, size)); 4/4/16 Programação Imperativa 8
Soma de matrizes Já que uma matriz é um array de arrays, para somar todos os elementos de uma matriz, podemos recorrer à soma de arrays: int ints2_sum(int **m, int r, int c) int result = 0; for (int i = 0; i < r; i++) result += ints_sum(m[i], c); return result; 4/4/16 Programação Imperativa 9
Matrizes parciais e submatrizes Na verdade, a função ints2_sum calcula a somas dos c primeiros elementos das primeiras r linhas da matriz m, presumindo que a matriz tem pelo menos r linhas e pelo menos c colunas. Podemos considerar que os primeiros c elementos das primeiras r linhas de uma matriz com pelo menos r linhas e pelo menos c colunas constituem uma matriz parcial. Outra coisa seria uma submatriz, formada por subarrays contíguos com o mesmo deslocamento e o mesmo tamanho. 7 4 24 3 4 9 0 6 2 6 16 2 3 34 5 4 12 6 5 1 7 4 24 3 4 9 0 6 2 6 16 2 3 34 5 4 12 6 5 1 A amarelo, uma matriz parcial. A cor de laranja, uma submatriz. 4/4/16 Programação Imperativa 10
Somando submatrizes Para somar submatrizes, precisamos de uma nova função. Ei-la: m é a matriz, dv é o deslocamento vertical, dh é o deslocamento horizontal, r é o número de linhas e c é o número de colunas: int ints2_sub_sum(int **m, int dv, int dh, int r, int c) Note bem: r é o número de linhas da submatriz, int result = 0; não da matriz m, e analogamente para c. for (int i = 0; i < r; i++) result += ints_sum(m[i+dv]+dh, c); return result; A função presume que a matriz m tem pelo menos r+dv linhas e c+dh colunas. 4/4/16 Programação Imperativa 11
Somas acumuladas de matrizes O problemas da acumulação de matrizes consiste em, dada uma matriz m com r linhas e c colunas, calcular uma outra matriz acc com r+1 linhas e c+1 colunas tal que acc[i][j] é a soma dos j primeiros elementos das i primeiras linhas de a. Com certeza NÃO queremos programar assim: void ints2_accumulate_brute(int **a, int r, int c, int **b) for (int i = 0; i < r+1; i++) for (int j = 0; j < c+1; j++) b[i][j] = ints2_sum(a, i, j); 4/4/16 Programação Imperativa 12
Exercício Programe a acumulação (bidimensional) de matrizes, usando como inspiração a acumulação (linear) de arrays: void ints2_accumulate(int **a, int r, int c, int **b)... Como aquecimento, resolva o problema primeiro no Excel: como transformar a folha da esquerda na folha da direita (sem usar a função SUM)? 4/4/16 Programação Imperativa 13
Teste unitário da acumulação bidimensional Podemos recorrer à soma das matrizes parciais para validar a nossa função de acumulação: void unit_test_ints2_accumulate(void) int z[4][6] = 5,1,3,4,7,1, //21 4,3,1,4,9,2, //23 1,4,2,3,7,1, //18 8,1,3,9,4,2; //27 int **a = ints2_new(4, 6); ints_copy(z[0], 24, a[0]); int **b = ints2_new(5, 7); ints2_accumulate(a, 4, 6, b); for (int i = 0; i < 5; i++) for (int j = 0; j < 7; j++) assert(b[i][j] == ints2_sum(a, i, j)); 4/4/16 Programação Imperativa 14
Soma de submatrizes via acumulação Usando a matriz de acumulação, que terá sido précalculada, podemos calcular a soma de submatrizes com apenas três adições ou subtrações. Fica como exercício: int ints2_sub_sum_via_accumulation( int **m, int dv, int dh, int r, int c) Note bem: m é a matriz de acumulação. return...; Em situações onde seja preciso calcular muitas vezes somas de submatrizes, esta técnica é muito útil. 4/4/16 Programação Imperativa 15
Teste unitário da soma de submatrizes Observe: void unit_test_ints2_sub_sum_via_accumulation(void) const int z[4][6] = 5,1,3,4,7,1, //21 4,3,1,4,9,2, //23 1,4,2,3,7,1, //18 8,1,3,9,4,2; //27 int **a = ints2_new(4, 6); ints_copy(z[0], 24, a[0]); assert(ints2_sum(a, 4, 6) == 89); assert(ints2_sum(a, 2, 5) == 41); assert(ints2_sub_sum(a, 0, 0, 4, 6) == 89); assert(ints2_sub_sum(a, 1, 1, 2, 2) == 10); assert(ints2_sub_sum(a, 2, 3, 2, 3) == 26); assert(ints2_sub_sum(a, 0, 1, 4, 1) == 9); int **b = ints2_new(5, 7); ints2_accumulate(a, 4, 6, b); assert(ints2_sub_sum_via_accumulation(b, 0, 0, 4, 6) == 89); assert(ints2_sub_sum_via_accumulation(b, 0, 0, 2, 5) == 41); assert(ints2_sub_sum_via_accumulation(b, 0, 0, 4, 6) == 89); assert(ints2_sub_sum_via_accumulation(b, 1, 1, 2, 2) == 10); assert(ints2_sub_sum_via_accumulation(b, 2, 3, 2, 3) == 26); assert(ints2_sub_sum_via_accumulation(b, 0, 1, 4, 1) == 9); 4/4/16 Programação Imperativa 16