8. cvičení

Matice lze v počítači reprezentovat pomocí dvourozměrného pole. Pole s m řádky a n sloupci (matice \(m \times n\)) lze deklarovat takto:

float matice[m][n];

Ve skutečnosti jsou prvky dvourozměrného pole uloženy jako (jednorozměrné) pole délky \(m \times n\), přičemž prvky jsou uloženy po řádcích. Dvourozměrné pole však zahrnuje indexování prvků pomocí dvojice indexů. Deklaraci lze snadno zobecnit pro libovolnou dimenzi pole, např. 4-rozměrné pole \(2 \times 3 \times 4 \times 5\) můžeme deklarovat takto:

float pole[2][3][4][5];

Pro přístup k prvkům vícerozměrného pole je třeba použít odpovídající počet hranatých závorek. Např. \(i,j\)-tý prvek výše deklarované matice je matice[i][j].

Dvourozměrné pole je zobecněním jednorozměrného pole, ale předávání dvourozměrného pole jako parametr funkce je o poznání komplikovanější. Typ parametru totiž nemůže být float* (čili adresa prvního prvku), protože tím by se ztratila informace o indexování pomocí dvojice indexů. Parametr funkce je tedy potřeba specifikovat takto:

void funkce(int m, int n, float matice[m][n]);

Pomocí dvojice hranatých závorek říkáme, že matice je matice o m řádcích a n sloupcích, přičemž m a n jsou dříve specifikované parametry funkce. Na čísle v první hranaté závorce však nezáleží, podstatný je pouze počet sloupců. Funkci tedy lze ekvivalentně deklarovat takto:

void funkce(int m, int n, float matice[][n]);

nebo takto:

void funkce(int m, int n, float matice[100][n]);

Pro přehlednost je však vhodné používat výhradně první variantu, kde počet řádků odpovídá skutečnosti.

Tento způsob předávání vícerozměrného pole lze zkonkrétnit i pro jednorozměrné pole:

void funkce(int n, float pole[n]);

Nebo ekvivalentně:

void funkce(int n, float pole[]);

Probrané příklady

  1. Funkce pro součet a rozdíl dvou matic. Následující program ukazuje práci s dvourozměrným polem – načítání prvků, předání do funkce, přístup k prvkům během výpočtu a výpis prvků.

    #include <stdio.h>
    
    void secti_matice(int r, int s, const float m1[r][s], const float m2[r][s], float m3[r][s])
    {
        for (int i = 0; i < r; i++)
        for (int j = 0; j < s; j++)
        {
            m3[i][j] = m1[i][j] + m2[i][j];
        }
    }
    
    int main()
    {
        printf("Zadej pocet radku a pocet sloupcu: ");
        int m, n;
        scanf("%d%d", &m, &n);
    
        // matice m x n
        float A[m][n];
        float B[m][n];
        float C[m][n];
    
        printf("Zadej prvky matice A (po radcich):\n");
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                scanf("%f", &A[i][j]);
            }
        }
    
        printf("Zadej prvky matice B (po radcich):\n");
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                scanf("%f", &B[i][j]);
            }
        }
    
        // spocitat C = A + B
        secti_matice(m, n, A, B, C);
    
        // vypis matice C
        printf("Matice C = A + B je:\n");
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                printf("%f\t", C[i][j]);
            }
            printf("\n");
        }
    }
    
  2. Funkce pro násobení matice a vektoru. Načítání prvků matice a vektoru lze snadno převzít z předchozích příkladů, proto zde uvádím pouze samotnou funkci pro výpočet součinu \(c = A \cdot b\), kde \(A\) je matice \(r \times s\), \(b\) je vektor délky \(s\) a \(c\) je vektor délky \(r\).

    void matice_krat_vektor(int r, int s, float A[r][n], float b[s], float c[r])
    {
        for (int i = 0; i < r; i++) {
            // vypocet skalarniho soucinu i-teho radku A a vektoru b
            float soucet = 0;
            for (int j = 0; j < s; j++) {
                soucet += A[i][j] * b[j];
            }
            // zapis do vektoru c
            c[i] = soucet;
        }
    }
    
  3. Funkce pro násobení dvou (obdélníkových) matic. Následující funkce spočítá součin \(C = A \cdot B\), kde \(A\) je matice \(r \times s\), \(B\) je matice \(s \times t\) a \(C\) je matice \(r \times t\).

    void soucin_matic(int r, int s, int t, const float A[r][s], const float B[s][t], float C[r][t])
    {
        for (int i = 0; i < r; i++)
        for (int j = 0; j < t; j++)
        {
            // napred vynulovat vysledek
            C[i][j] = 0;
            // vypocet skalarniho soucinu i-teho radku A a j-teho sloupce B
            for (int k = 0; k < s; k++)
                C[i][j] += A[i][k] * B[k][j]
        }
    }