Serwis Edukacyjny
w I-LO w Tarnowie
obrazek

Materiały dla uczniów liceum

  Wyjście       Spis treści       Wstecz       Dalej  

obrazek

Autor artykułu: mgr Jerzy Wałaszek

©2019 mgr Jerzy Wałaszek
I LO w Tarnowie

obrazek

Równania

Macierze

SPIS TREŚCI
Podrozdziały

Definicje

W matematyce macierz (ang. matrix) jest prostokątną tablicą liczb, którą zwykle przedstawiamy następująco:

Liczby w macierzy ułożone są w poziomych wierszach oraz pionowych kolumnach. Liczba wierszy i liczba kolumn określa rozmiar macierzy (ang. matrix size). Macierze zapisujemy zwykle dużymi literami alfabetu, a w indeksie podajemy ich rozmiar. Liczby w macierzy są jej wyrazami (ang. matrix elements/entries) i posiadają indeksy określające numer wiersza i numer kolumny, w których się znajdują. Wyrazy macierzy zapisujemy małymi literami alfabetu:

Macierz zbudowaną z jednego wiersza lub jednej kolumny nazywamy wektorem (ang. vector matrix). Wektory zapisujemy skrótowo jako:

Macierz o jednakowej liczbie wierszy i kolumn nazywamy macierzą kwadratową (ang. square matrix):

Wyrazy macierzy o równych numerach wiersza i kolumny nazywamy przekątną główną macierzy (ang. matrix main diagonal), a liczba wierszy lub kolumn nazywana jest wtedy stopniem tej macierzy. Macierz kwadratowa stopnia czwartego wygląda następująco:

Macierz zerowa (ang. zero matrix lub null matrix) posiada wszystkie elementy zerowe:

Macierz jednostkowa, tożsamościowa, identycznościowa (ang. unit matrix lub identity matrix) jest macierzą kwadratową, której elementy przekątnej głównej są równe 1, a wszystkie pozostałe są równe 0:

Podmacierzą (ang. submatrix) nazywamy macierz, która powstaje z macierzy wyjściowej po usunięciu dowolnej liczby kolumn i wierszy:

Na początek:  podrozdziału   strony 

Tablice dynamiczne w języku C++

 
Macierz w języku C++ jest reprezentowana przez tablicę. Tablicę możemy deklarować statycznie lub dynamicznie. Deklaracja statyczna wymaga znajomości rozmiaru tablicy w momencie tworzenia programu.

Poniższy program deklaruje tablicę o rozmiarze 10×20 i wypełnia ją liczbami przypadkowymi o zakresie od 0 do 99.

Przykładowy program w języku C++
// Macierze
// (C)2019 mgr Jerzy Wałaszek
// Metody numeryczne
//---------------------------

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <ctime>

using namespace std;

// Program główny
//---------------

int main()
{
    setlocale(LC_ALL,"");

    cout << "Tablica statyczna o rozmiarze 10x20" << endl
         << "-----------------------------------" << endl << endl;

    // Definicja tablicy statycznej

    unsigned int A[10][20];

    // Indeksy elementów

    unsigned i,j;

    // Inicjujemy generator pseudolosowy

    srand(time(NULL));

    // Tablicę wypełniamy liczbami pseudolosowymi z zakresu 0...99

    for(i = 0; i < 10; i++) // Wiersze
        for(j = 0; j < 20; j++) // Kolumny
            A[i][j] = rand() % 100;

    // Weświetlamy wyniki

    for(i = 0; i < 10; i++)
    {

        for(j = 0; j < 20; j++) cout << setw(3) << A[i][j];
        cout << endl;
    }

    cout << endl;

    return 0;
}
Wynik
Tablica statyczna o rozmiarze 10x20
-----------------------------------

 36 34 35 86  2 27 61 50 55 52 59  4 34 77 98 43 94 59  7 73
 15 37  5 96 23 31 90 61 13 13 48  0 74 15 52 46 33 75 14 24
 32 39 11 28 43 22 10 37 81 93 12 70 81 18 28 23 47 27 62 39
 38 55 60 29 12 30 66 74 52 79 48 98 80 87 25 47 95  3 42 18
 76 40 70 87 16 34 20  4 13 47 37 76 51 46 83  5 39 36 37 59
 34 89 37 27  2 42 59 29 87 26 35 48 61 30 20 30 33 69 27 66
 29 28 68 72 44 71 54 86 86 32 21 21 32 19 95 14 11 62 28 41
 71 58 83 73 49  2 36 37 72  0 39 59 44 49 73 95 64 44 58 74
 90 62  6 20  1 13 24 82 90 82 52 88 22 59 60 26 67  9 57 85
 24 34 66 49 14 35 37 21 84  7 25 66 31 19 81 15 79  5  7 76

Pamiętaj, iż w języku C++ indeksy rozpoczynają się od 0, zatem element A[0][0] jest elementem w pierwszym wierszu i w pierwszej kolumnie macierzy.

Tablicę dynamiczną można stworzyć na kilka sposobów. Pierwszy z nich polega na zarezerwowaniu odpowiedniego obszaru pamięci i umieszczenie w nim tablicy, Po wykorzystaniu tablicy zarezerwowany dla niej obszar pamięci należy zwolnić.

Drugi program pokazuje, jak to zrobić w języku C++:

Przykładowy program w języku C++
// Macierze
// (C)2019 mgr Jerzy Wałaszek
// Metody numeryczne
//---------------------------

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <ctime>

using namespace std;

// Program główny
//---------------

int main()
{
    // Inicjujemy generator pseudolosowy

    srand(time(NULL));

    // Generujemy rozmiar tablicy:
    // n - liczba wierszy:  5...15
    // m - liczba kolumn:  10...20

    int n =  5 + rand() % 11;
    int m = 10 + rand() % 11;

    // Tworzymy wskaźnik do wskaźników liczb całkowitych

    int ** A;

    // Tworzymy dynamicznie obszar na tablicę n wskaźników

    A = new int * [n];

    // Tworzymy dynamiczne tablice dla każdego wiersza

    int i,j;

    for(i = 0; i < n; i++) A[i] = new int [m];

    setlocale(LC_ALL,"");

    cout << "Tablica dynamiczna o rozmiarze " << n << "x" << m << endl
         << "------------------------------------" << endl << endl;

    // Tablicę wypełniamy liczbami pseudolosowymi z zakresu 0...99

    for(i = 0; i < n; i++) // Wiersze
        for(j = 0; j < m; j++) // Kolumny
            A[i][j] = rand() % 100;

    // Wyświetlamy wyniki

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

        for(j = 0; j < m; j++) cout << setw(3) << A[i][j];
        cout << endl;
    }

    cout << endl;

    // Usuwamy tablice wierszy

    for(i = 0; i < n; i++) delete [] A[i];

    // Usuwamy tablicę wskaźników

    delete [] A;

    return 0;
}
Wynik
Tablica dynamiczna o rozmiarze 12x19
------------------------------------

 22 61 20 80 10 97 86 84 41 26 64 66  7 92 85 76 55  0 90
 44 76 77 14 51 18 16 63 42 94 93 27 43 15 35  6  7  6 36
  2 51 47 67 70 71 35 17  3 15 44 77 23  4 12 45 36  5 95
 66  9 78 67 68 40 88 24 62 36 43 19 54  3 20 95 56 18 80
 95 91 76 65 83 33 61 69 37 56 12  0 21 89 95  9 28  4  4
 77 14 84 47 32 42 40 91 20 98 26 42 83  3 55 82 95 18 79
 92  3 56 94 50 41 47 36 51 26 21 21 17 74 52 52 12 53 91
 37 45 97  4 84 41  8 34  8 75 96 37 71 68 38 28 40 38 88
 90 71 78 94 85 36 94 95 46 30 97 95  1 25 97 83 36 50 47
 21 21 78 61 46 73 14 73  9 70 97 15 81 81 70  5 45 44 87
 30  7 50 59 40 42  4 72 59 68 94 56 88 20 19 57 27 71 62
 86 95 90 57 37  3 19 18 54 62 15 19 17 71 67 86 58 71 18

Macierz może również zostać odwzorowana jako klasa języka C++. W klasie można zdefiniować różne operacje na zawartej w niej macierzy. Plik definiujący klasę można używać jako plik nagłówkowy w programach przetwarzających macierze. Zaletą klasy jest to, iż prosto możemy zdefiniować w programie wiele różnych macierzy.

Poniższy program demonstruje przykładową prostą klasę macierzy liczb całkowitych. Klasa ta wspiera operacje przypisywania wartości elementom macierzy oraz odczytywania wartości elementów (dla prostoty klasa nie sprawdza błędów). Zwróć uwagę, iż zarezerwowano obszar dla tablicy jednowymiarowej. Tak też można.

Przykładowy program w języku C++
// Macierze
// (C)2019 mgr Jerzy Wałaszek
// Metody numeryczne
//---------------------------

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <ctime>

using namespace std;

// Definicja klasy, może być w pliku nagłówkowym
//----------------------------------------------

#ifndef _matrix_class
    #define _matrix_class
    class matrix
    {
        private:
            int n,m;  // Rozmiar macierzy
            int * A;  // Elementy
        public:
            matrix(int r, int c); // Konstruktor
            ~matrix();            // Destruktor
            void setv(int r, int c, int v);
            int  getv(int r, int c);
    };

    // Konstruktor - tworzy macierz o podanych wymiarach:
    // r - liczba wierszy
    // c - liczba kolumn
    //---------------------------------------------------
    matrix::matrix(int r, int c)
    {
        n = r;
        m = c;
        A = new int [n * m];
    }

    // Destruktor - usuwa macierz i zwraca zajętą przez nią pamięć
    //------------------------------------------------------------
    matrix::~matrix()
    {
        delete [] A;
    }

    // Ustawia wartość elementu
    // r - numer wiersza
    // c - numer kolumny
    // v - wartość dla elementu
    //-------------------------
    void matrix::setv(int r, int c, int v)
    {
        A[r * m + c] = v;
    }

    // Zwraca wartość elementu
    // r - numer wiersza
    // c - numer kolumny
    //-------------------------
    int matrix::getv(int r, int c)
    {
        return A[r * m + c];
    }
#endif // _matrix_class

// Program główny
//---------------

int main()
{
    // Inicjujemy generator pseudolosowy

    srand(time(NULL));

    // Generujemy rozmiar tablicy:
    // n - liczba wierszy:  5...15
    // m - liczba kolumn:  10...20

    int n =  5 + rand() % 11;
    int m = 10 + rand() % 11;

    // Tworzymy macierz
    matrix A(n,m);

    // Macierz wypełniamy liczbami od -99 do 99

    int i,j;

    for(i = 0; i < n; i++)
        for(j = 0; j < m; j++) A.setv(i,j,-99+rand() % 199);

    // Wyświetlamy macierz

    setlocale(LC_ALL,"");

    cout << "Klasa macierzy o rozmiarze " << n << "x" << m << endl
         << "--------------------------------" << endl << endl;
    for(i = 0; i < n; i++)
    {
        for(j = 0; j < m; j++) cout << setw(4) << A.getv(i,j);
        cout << endl;
    }

    cout << endl;

    return 0;
}
Wynik
Klasa macierzy o rozmiarze 10x15
--------------------------------

  55 -98 -58  16  10 -13  87  26 -51 -20   0  20 -76  68 -91
   0  20 -76  68 -91 -85  -6  96  75  96  32 -38 -18 -79 -92
  32 -38 -18 -79 -92  73  99  62 -63  37  73 -65 -19  30  17
  73 -65 -19  30  17 -29  75  55  57  99  85  35 -36  73 -43
  85  35 -36  73 -43 -35 -57 -45 -35  83 -89   4  65   6  10
 -89   4  65   6  10 -27  67  64 -49 -39 -95 -57 -75 -54  60
 -95 -57 -75 -54  60 -44  86  22 -16  14  61 -80 -57  18 -40
  61 -80 -57  18 -40 -46 -69  25 -89   7  -8  -3  33 -80  27
  -8  -3  33 -80  27  78 -78  48  85 -90  54  84  75 -94  59
  54  84  75 -94  59  48 -60  -2  43 -22  78  39 -94 -20  52

Lepszym pomysłem jest wykorzystanie klasy szablonowej. Klasa szablonowa nie określa typu elementów - robi to program użytkownika w momencie tworzenia zmiennej na podstawie szablonu klasy. Dzięki temu możemy w prosty sposób otrzymać macierz o dowolnym typie elementów.

Poniższy przykład programu definiuje klasę szablonową macierzy, następnie na jej podstawie tworzy macierz liczb typu double, wypełnia ją pseudolosowymi wartościami i wyświetla jej zawartość. Wszystko jest maksymalnie uproszczone.

Przykładowy program w języku C++
// Macierze
// (C)2019 mgr Jerzy Wałaszek
// Metody numeryczne
//---------------------------

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <ctime>

using namespace std;

// Definicja klasy, może być w pliku nagłówkowym
//----------------------------------------------

#ifndef _matrix_class
    #define _matrix_class

    template <class T> class matrix
    {
        private:
            int n,m;  // Rozmiar macierzy
            T * A;    // Elementy
        public:
            matrix(int r, int c); // Konstruktor
            ~matrix();            // Destruktor
            void setv(int r, int c, T v);
            T  getv(int r, int c);
    };

    // Konstruktor - tworzy macierz o podanych wymiarach:
    // r - liczba wierszy
    // c - liczba kolumn
    //---------------------------------------------------
    template <class T> matrix<T>::matrix(int r, int c)
    {
        n = r;
        m = c;
        A = new T [n * m];
    }

    // Destruktor - usuwa macierz i zwraca zajętą przez nią pamięć
    //------------------------------------------------------------
    template <class T> matrix<T>::~matrix()
    {
        delete [] A;
    }

    // Ustawia wartość elementu
    // r - numer wiersza
    // c - numer kolumny
    // v - wartość dla elementu
    //-------------------------
    template <class T> void matrix<T>::setv(int r, int c, T v)
    {
        A[r * m + c] = v;
    }

    // Zwraca wartość elementu
    // r - numer wiersza
    // c - numer kolumny
    //-------------------------
    template <class T> T matrix<T>::getv(int r, int c)
    {
        return A[r * m + c];
    }

#endif // _matrix_class

// Program główny
//---------------

int main()
{
    // Inicjujemy generator pseudolosowy

    srand(time(NULL));

    // Generujemy rozmiar tablicy:
    // n - liczba wierszy: 5...15
    // m - liczba kolumn:  5...15

    int n = 5 + rand() % 11;
    int m = 5 + rand() % 11;

    // Tworzymy macierz

    matrix<double> A(n,m);

    // Macierz wypełniamy liczbami pseudolosowymi od 0 do 9.99

    int i,j;

    for(i = 0; i < n; i++)
        for(j = 0; j < m; j++) A.setv(i,j,(rand() % 1000) / 100.0);

    // Wyświetlamy macierz

    setlocale(LC_ALL,"");

    cout << setprecision(2) << fixed;
    cout << "Klasa szablonowa macierzy o rozmiarze " << n << "x" << m << endl
         << "-------------------------------------------" << endl << endl;
    for(i = 0; i < n; i++)
    {
        for(j = 0; j < m; j++) cout << setw(5) << A.getv(i,j);
        cout << endl;
    }

    cout << endl;

    return 0;
}
Wynik
Klasa szablonowa macierzy o rozmiarze 12x13
-------------------------------------------

8.40 8.88 4.15 1.27 2.23 5.34 6.05 5.91 7.75 6.53 6.92 0.58 0.24
1.01 2.01 6.73 4.22 8.37 4.53 7.18 2.58 9.83 7.24 4.40 4.09 2.83
7.34 9.16 5.37 0.04 7.90 0.38 0.02 9.60 8.33 1.45 8.97 5.95 8.09
3.31 8.59 5.02 2.99 4.04 6.39 4.76 6.70 7.69 5.96 8.54 6.21 5.00
2.14 2.55 7.51 7.34 1.31 0.74 4.13 6.64 7.95 7.63 4.91 6.78 5.03
2.59 8.26 9.01 8.17 3.84 1.33 8.87 8.77 3.80 1.63 1.80 6.70 5.81
1.81 0.43 7.87 9.33 1.05 8.98 3.40 1.15 8.70 1.86 1.76 8.26 7.28
0.30 9.20 0.64 2.78 1.57 2.18 8.97 0.18 2.54 9.10 0.05 7.61 2.92
0.13 0.87 0.76 0.58 2.92 6.16 2.54 8.96 7.55 6.84 6.64 1.27 4.10
5.87 3.61 4.50 7.66 5.22 9.93 1.97 2.49 4.83 3.83 5.78 7.92 0.50
7.69 5.46 3.37 0.10 3.01 0.57 0.68 4.88 1.74 2.69 0.73 3.01 5.29
6.00 5.70 3.86 0.25 6.60 8.99 9.83 8.55 0.54 2.07 4.44 5.58 1.42

W dalszej części artykułu będziemy wykorzystywali klasę szablonową z  odpowiednimi modyfikacjami, ponieważ jest najbardziej ogólna.

Na początek:  podrozdziału   strony 

Wprowadzanie danych do macierzy

Podamy tutaj proste sposoby wprowadzania danych do programu przetwarzającego macierze. Dane można umieszczać bezpośrednio w programie, w pliku lub wprowadzać je przez strumień wejścia.

Dane umieszczone w programie nie są specjalnie wygodnym sposobem definiowania zawartości macierzy, ponieważ zmiana danych wymaga modyfikacji programu. Niemniej jednak ten sposób może być czasem przydatny.

Poniższy program tworzy macierz jednostkową i wyświetla jej zawartość:

Przykładowy program w języku C++
// Macierze
// (C)2019 mgr Jerzy Wałaszek
// Metody numeryczne
//---------------------------

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <ctime>

using namespace std;

// Definicja klasy, może być w pliku nagłówkowym
//----------------------------------------------

#ifndef _matrix_class
    #define _matrix_class

    template <class T> class matrix
    {
        private:
            int n,m,s;  // Rozmiar macierzy
            T * A;    // Elementy
        public:
            matrix(int r, int c); // Konstruktor
            ~matrix();            // Destruktor
            void makeIdent(void); // Macierz jednostkowa
            T  getv(int r, int c);
    };

    // Konstruktor - tworzy macierz o podanych wymiarach:
    // r - liczba wierszy
    // c - liczba kolumn
    //---------------------------------------------------
    template <class T> matrix<T>::matrix(int r, int c)
    {
        n = r;
        m = c;
        s = n * m;
        A = new T [s];
    }

    // Destruktor - usuwa macierz i zwraca zajętą przez nią pamięć
    //------------------------------------------------------------
    template <class T> matrix<T>::~matrix()
    {
        delete [] A;
    }

    // Tworzy macierz jednostkową
    //-------------------------
    template <class T> void matrix<T>::makeIdent()
    {
        int i;
        for(i = 0; i < s; i++) A[i] = (T) 0;
        for(i = 0; i < n; i++) A[i * m + i] = (T) 1;
    }

    // Zwraca wartość elementu
    // r - numer wiersza
    // c - numer kolumny
    //-------------------------
    template <class T> T matrix<T>::getv(int r, int c)
    {
        return A[r * m + c];
    }

#endif // _matrix_class

// Program główny
//---------------

int main()
{
    // Inicjujemy generator pseudolosowy

    srand(time(NULL));

    // Generujemy rozmiar macierzy:
    // n - liczba wierszy i kolumn: 10...20

    int n,i,j;

    n = 10 + rand() % 11;

    // Tworzymy macierz kwadratową

    matrix<int> A(n,n);

    // Tworzymy macierz jednostkową

    A.makeIdent();

    // Wyświetlamy macierz

    setlocale(LC_ALL,"");

    cout << "Macierz jednostkowa o rozmiarze " << n << "x" << n << endl
         << "-------------------------------------" << endl << endl;
    for(i = 0; i < n; i++)
    {
        for(j = 0; j < n; j++) cout << setw(2) << A.getv(i,j);
        cout << endl;
    }

    cout << endl;

    return 0;
}
Wynik
Macierz jednostkowa o rozmiarze 20x20
-------------------------------------

1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1

Następne dwie metody odczytują dane dla macierzy ze strumienia, Pierwsza metoda odczytuje dane z pliku dane.txt, który należy umieścić w katalogu projektowym, jeśli pracujesz w edytorze Code Blocks, lub w katalogu z programem, jeśli uruchamiasz program z poziomu konsoli lub menedżera plików.

Dane są skonstruowane następująco:

Przykładowe dane:

5 6
1 6 8 7 5 6
8 8 9 2 1 0
3 1 4 7 2 1
2 2 0 7 9 1
0 0 0 2 2 2

Przekopiuj plik dane.txt do odpowiedniego katalogu.

Poniższy program tworzy macierz na podstawie danych z pliku. Zakładamy, że dane są liczbami całkowitymi bez znaku (typ macierzy musi się zgadzać z typem danych w pliku). Następnie program wyświetla zawartość wczytanej macierzy.

Przykładowy program w języku C++
// Macierze
// (C)2019 mgr Jerzy Wałaszek
// Metody numeryczne
//---------------------------

#include <iostream>
#include <fstream>
#include <iomanip>

using namespace std;

// Definicja klasy, może być w pliku nagłówkowym
//----------------------------------------------

#ifndef _matrix_class
    #define _matrix_class

    template <class T> class matrix
    {
        private:
            int n,m,s;  // Rozmiar macierzy
            T * A;      // Elementy
        public:
            matrix();             // Konstruktor
            ~matrix();            // Destruktor
            T  getv(int r, int c);
            int rows();           // Zwraca liczbę wierszy macierzy
            int cols();           // Zwraca liczbę kolumn macierzy
    };

    // Konstruktor - tworzy macierz z pliku dane.txt
    //----------------------------------------------
    template <class T> matrix<T>::matrix()
    {
        ifstream fin;  // Strumień wejścia z pliku

        fin.open("dane.txt",ifstream::in); // Otwieramy strumień do odczytu

        fin >> n >> m; // Odczytujemy ze strumienia wymiary macierzy

        s = n * m;

        A = new T [s]; // Rezerwujemy pamięć na macierz

        for(int i = 0; i < s; i++) fin >> A[i]; // Wczytujemy wyrazy do macierzy

        fin.close();   // Zamykamy strumień
    }

    // Destruktor - usuwa macierz i zwraca zajętą przez nią pamięć
    //------------------------------------------------------------
    template <class T> matrix<T>::~matrix()
    {
        delete [] A;
    }

    // Zwraca wartość elementu
    // r - numer wiersza
    // c - numer kolumny
    //-------------------------
    template <class T> T matrix<T>::getv(int r, int c)
    {
        return A[r * m + c];
    }

    // Zwraca liczbę wierszy w macierzy
    //---------------------------------
    template <class T> int matrix<T>::rows()
    {
        return n;
    }

    // Zwraca liczbę kolumn w macierzy
    //---------------------------------
    template <class T> int matrix<T>::cols()
    {
        return m;
    }

#endif // _matrix_class

// Program główny
//---------------

int main()
{
    // Tworzymy macierz

    matrix<int> A;

    // Wyświetlamy macierz

    int i,j;

    setlocale(LC_ALL,"");

    cout << "Macierz z pliku o rozmiarze " << A.rows() << "x" << A.cols() << endl
         << "---------------------------------" << endl << endl;
    for(i = 0; i < A.rows(); i++)
    {
        for(j = 0; j < A.cols(); j++) cout << setw(4) << A.getv(i,j);
        cout << endl;
    }

    cout << endl;

    return 0;
}
Wynik
Macierz z pliku o rozmiarze 10x15
---------------------------------

   1   2   3   4   5   6   7   8   9   0   1   2   3   4   5
   7   0   9   6   4   7   2   5   3   1   0   2   8   2   3
   8   8   6   2   3   1   0   8   5   4   8   2   9   9   2
   9   6   9   2   3   2   1   9   6   8   2   3   4   5   1
   8   9   5   6   3   2   2   2   6  11   5   7   9  12   6
   0   7   8   2   7   3   5   5   1   8   9   3   3   2   0
   9   7   2   5   5   5   2   2   2   1   9   8   0   0   0
   4 444   7   6   8   9   2   3   6   8   8 888  92  43  15
   1   3  66  87  51  62  88  92  83  99  11  13   6  55   6
   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15

 

Kolejny program jest właściwie identyczny z poprzednim. Różnica polega na tym, iż dane dla macierzy odczytywane są ze standardowego strumienia. Poniższe dane skopiuj do schowka, uruchom program i wklej je do okna konsoli  (Windows: Ctrl+V, Linuks Ctrl+Shift+V):

Dane wejściowe:

10 15
126 22 36 4 54 6 7 88 96 0 13 25 3 47 52
75 0 95 62 46 73 21 5 32 17 0 27 8 29 34
82 85 6 21 343 16 28 8 5 44 83 29 911 93 23
99 655 96 222 32 24 1 96 68 8 21 3 4 53 18
81 93 598 6 32 25 29 21 63 11 56 71 92 12 6
0 734 8 27 7 37 5 53 1 89 9 34 37 22 0
929 7 26 5 533 5 2 29 22 15 92 83 47 28 0
4 444 7 6 8 9 26 3 69 86 87 888 92 43 15
51 34 66 87 51 62 88 92 83 99 11 13 6 55 6
19 2 3 4 5 6 74 8 9 10 11 12 13 14 15
Przykładowy program w języku C++
// Macierze
// (C)2019 mgr Jerzy Wałaszek
// Metody numeryczne
//---------------------------

#include <iostream>
#include <iomanip>

using namespace std;

// Definicja klasy, może być w pliku nagłówkowym
//----------------------------------------------

#ifndef _matrix_class
    #define _matrix_class

    template <class T> class matrix
    {
        private:
            int n,m,s;  // Rozmiar macierzy
            T * A;    // Elementy
        public:
            matrix();             // Konstruktor
            ~matrix();            // Destruktor
            T  getv(int r, int c);
            int rows();           // Zwraca liczbę wierszy macierzy
            int cols();           // Zwraca liczbę kolumn macierzy
    };

    // Konstruktor - tworzy macierz ze strumienia cin
    //-----------------------------------------------
    template <class T> matrix<T>::matrix()
    {
        cin >> n >> m; // Odczytujemy ze strumienia wymiary macierzy

        s = n * m;

        A = new T [s]; // Rezerwujemy pamięć na macierz

        for(int i = 0; i < s; i++) cin >> A[i]; // Wczytujemy wyrazy do macierzy

    }

    // Destruktor - usuwa macierz i zwraca zajętą przez nią pamięć
    //------------------------------------------------------------
    template <class T> matrix<T>::~matrix()
    {
        delete [] A;
    }

    // Zwraca wartość elementu
    // r - numer wiersza
    // c - numer kolumny
    //-------------------------
    template <class T> T matrix<T>::getv(int r, int c)
    {
        return A[r * m + c];
    }

    // Zwraca liczbę wierszy w macierzy
    //---------------------------------
    template <class T> int matrix<T>::rows()
    {
        return n;
    }

    // Zwraca liczbę kolumn w macierzy
    //---------------------------------
    template <class T> int matrix<T>::cols()
    {
        return m;
    }

#endif // _matrix_class

// Program główny
//---------------

int main()
{
    // Wyświetlamy macierz

    int i,j;

    setlocale(LC_ALL,"");

    cout << "Wpisz dane dla macierzy:" << endl;

    // Tworzymy macierz z wczytaniem jej ze strumienia

    matrix<int> A;

    cout << endl
         << "Macierz ze strumienia o rozmiarze " << A.rows() << "x" << A.cols() << endl
         << "---------------------------------------" << endl << endl;
    for(i = 0; i < A.rows(); i++)
    {
        for(j = 0; j < A.cols(); j++) cout << setw(4) << A.getv(i,j);
        cout << endl;
    }

    cout << endl;

    return 0;
}
Wynik
Wpisz dane dla macierzy:
10 15
126 22 36 4 54 6 7 88 96 0 13 25 3 47 52
75 0 95 62 46 73 21 5 32 17 0 27 8 29 34
82 85 6 21 343 16 28 8 5 44 83 29 911 93 23
99 655 96 222 32 24 1 96 68 8 21 3 4 53 18
81 93 598 6 32 25 29 21 63 11 56 71 92 12 6
0 734 8 27 7 37 5 53 1 89 9 34 37 22 0
929 7 26 5 533 5 2 29 22 15 92 83 47 28 0
4 444 7 6 8 9 26 3 69 86 87 888 92 43 15
51 34 66 87 51 62 88 92 83 99 11 13 6 55 6
19 2 3 4 5 6 74 8 9 10 11 12 13 14 15

Macierz ze strumienia o rozmiarze 10x15
---------------------------------------

 126  22  36   4  54   6   7  88  96   0  13  25   3  47  52
  75   0  95  62  46  73  21   5  32  17   0  27   8  29  34
  82  85   6  21 343  16  28   8   5  44  83  29 911  93  23
  99 655  96 222  32  24   1  96  68   8  21   3   4  53  18
  81  93 598   6  32  25  29  21  63  11  56  71  92  12   6
   0 734   8  27   7  37   5  53   1  89   9  34  37  22   0
 929   7  26   5 533   5   2  29  22  15  92  83  47  28   0
   4 444   7   6   8   9  26   3  69  86  87 888  92  43  15
  51  34  66  87  51  62  88  92  83  99  11  13   6  55   6
  19   2   3   4   5   6  74   8   9  10  11  12  13  14  15

W dalszej części artykułu będziemy stosować tę ostatnią metodę do wprowadzania danych macierzowych w przykładowych programach.

Na początek:  podrozdziału   strony 

Operacje na macierzach

Na macierzach można wykonywać różne operacje arytmetyczne, które krótko opisujemy poniżej:


Transpozycja

Jeśli w macierzy zamienimy wiersze z kolumnami to otrzymamy macierz transponowaną. Operację transponowania macierzy (ang. matrix transposition) zapisujemy dużą literą T w indeksie górnym:

Poniższy program wczytuje macierz ze standardowego wejścia i dokonuje jej transpozycji. Dane dla macierzy są pobierane ze strumienia wejściowego.

Przykładowe dane (przekopiuj je do schowka i wklej w programie):

Macierz:

Dane wejściowe:

4 6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
Przykładowy program w języku C++
// Macierze
// (C)2019 mgr Jerzy Wałaszek
// Metody numeryczne
//---------------------------

#include <iostream>
#include <iomanip>

using namespace std;

// Definicja klasy, może być w pliku nagłówkowym
//----------------------------------------------

#ifndef _matrix_class
    #define _matrix_class

    template <class T> class matrix
    {
        private:
            int s;                // Rozmiar macierzy
            T * A;                // Elementy
        public:
            int n,m;              // Liczba wierszy i kolumn
            matrix();             // Konstruktor
            ~matrix();            // Destruktor
            T  getv(int r, int c);
            void transpose();     // Dokonuje transpozycji macierzy
    };

    // Konstruktor - tworzy macierz ze strumienia cin
    //-----------------------------------------------
    template <class T> matrix<T>::matrix()
    {
        cin >> n >> m;   // Rozmiar macierzy
        s = n * m;
        A = new T [s]; // Rezerwujemy pamięć na macierz n x m

        for(int i = 0; i < s; i++) cin >> A[i]; // Wczytujemy wyrazy macierzy
    }

    // Destruktor - usuwa macierz i zwraca zajętą przez nią pamięć
    //------------------------------------------------------------
    template <class T> matrix<T>::~matrix()
    {
        delete [] A;
    }

    // Zwraca wartość elementu
    // r - numer wiersza
    // c - numer kolumny
    //-------------------------
    template <class T> T matrix<T>::getv(int r, int c)
    {
        return A[r * m + c];
    }

    // Transponuje macierz
    //--------------------
    template <class T> void matrix<T>::transpose()
    {
        T * B = new T[s];  // Tymczasowa macierz o tej samej liczbie elementów
        int i,j;
        for(i = 0; i < n; i++)
            for(j = 0; j < m; j++) B[j * n + i] = A[i * m + j];
        delete [] A;       // Usuwamy starą macierz
        A = B;             // Podmieniamy ją macierzą transponowaną
        swap(n,m);         // Zamieniamy wiersze z kolumnami
    }
#endif // _matrix_class

// Program główny
//---------------

int main()
{

    // Wczytujemy dane dla macierzy

    setlocale(LC_ALL,"");

    cout << "Wpisz dane dla macierzy:" << endl << endl;

    matrix<int> A;

    cout << endl
         << "Macierz przed transponowaniem ma wymiary " << A.n << " x " << A.m << endl
         << "----------------------------------------------" << endl << endl;
    for(int i = 0; i < A.n; i++)
    {
        for(int j = 0; j < A.m; j++) cout << setw(6) << A.getv(i,j);
        cout << endl;
    }

    // Transponujemy macierz

    A.transpose();

    cout << endl << endl
         << "Macierz po transponowaniu ma wymiary " << A.n << " x " << A.m << endl
         << "------------------------------------------" << endl << endl;
    for(int i = 0; i < A.n; i++)
    {
        for(int j = 0; j < A.m; j++) cout << setw(6) << A.getv(i,j);
        cout << endl;
    }

    cout << endl << endl;

    return 0;
}
Wynik
Wpisz dane dla macierzy:

4 6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

Macierz przed transponowaniem ma wymiary 4 x 6
----------------------------------------------

     1     2     3     4     5     6
     7     8     9    10    11    12
    13    14    15    16    17    18
    19    20    21    22    23    24


Macierz po transponowaniu ma wymiary 6 x 4
------------------------------------------

     1     7    13    19
     2     8    14    20
     3     9    15    21
     4    10    16    22
     5    11    17    23
     6    12    18    24

Mnożenie przez skalar

Macierze można mnożyć przez liczbę. Wynikiem takiego mnożenia jest macierz, której wszystkie elementy zostały pomnożone przez daną liczbę:


Dodawanie macierzy

Dodawać można macierze o tych samych wymiarach. Wynikiem dodawania jest macierz, której elementy są sumami odpowiadających sobie elementów obu macierzy:


Mnożenie macierzy

Mnożenie w przypadku macierzy jest trochę bardziej skomplikowane. Aby można było pomnożyć dwie macierze, muszą one spełniać pewien warunek, mianowicie, jeśli pierwsza macierz A ma m kolumn, to druga macierz B musi posiadać m wierszy:

An × m × Bm × p = Cn × p

Macierz wynikowa C posiada tyle wierszy, ile miała macierz A i tyle kolumn, ile miała macierz B.

Zasadę mnożenia najlepiej jest zrozumieć na prostym przykładzie. Mamy dwie macierze:

Macierz A ustawiamy po lewej stronie macierzy C, a macierz B ustawiamy ponad macierzą C:

                  1 3 5 7 9  
                B =   11 13 15 17 19  
                21 23 25 27 29  
                  31 33 35 37 39  
    0 2 4 6       ?   ?   ?   ?   ?  
 A =    8 10 12 14    C =   ? ? ? ? ?  
    16 18 20 22       ? ? ? ? ?  

Kolejne elementy cij macierzy C obliczamy jako sumę iloczynów kolejnych elementów z wiersza i-tego macierzy A przez kolejne elementy z kolumny j-tej macierzy B. Na przykład:

                  1 3 5 7 9  
                B =   11 13 15 17 19  
                21 23 25 27 29  
                  31 33 35 37 39  
    0 2 4 6       292   ?   ?   ?   ?  
 A =    8 10 12 14    C =   ? ? ? ? ?  
    16 18 20 22       ? ? ? ? ?  

W ten sam sposób obliczamy pozostałe elementy macierzy C.

                  1 3 5 7 9  
                B =   11 13 15 17 19  
                21 23 25 27 29  
                  31 33 35 37 39  
    0 2 4 6       292   316 340 364 388  
 A =    8 10 12 14    C =   804 892 980 1068 1156  
    16 18 20 22       1316 1468 1620 1772 1924  

Poniższy program mnoży dwie macierze i wyświetla wynik mnożenia. Dane wejściowe są odczytywane ze strumienia wejściowego. Dane macierzy poprzedzone są dwoma liczbami, które oznaczają liczbę wierszy i kolumn. Za tymi liczbami należy umieścić odpowiednią liczbę elementów dla macierzy. Elementy są liczbami całkowitymi. Program nie sprawdza poprawności danych.

Przykładowe dane (przekopiuj je do schowka i wklej w programie):

Macierze:

obrazek

Dane wejściowe:

3 4
0 2 4 6 8 10 12 14 16 18 20 22
4 5
1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39
Przykładowy program w języku C++
// Macierze
// (C)2019 mgr Jerzy Wałaszek
// Metody numeryczne
//---------------------------

#include <iostream>
#include <iomanip>

using namespace std;

// Definicja klasy, może być w pliku nagłówkowym
//----------------------------------------------

#ifndef _matrix_class
    #define _matrix_class

    template <class T> class matrix
    {
        private:
            int s;                // Rozmiar macierzy
            T * A;                // Elementy macierzy A
        public:
            int n,m;              // Liczba wierszy i kolumn
            matrix();             // Konstruktor
            matrix(int r, int c); // Konstruktor
            ~matrix();            // Destruktor
            T  getv(int r, int c);
            void multiply(matrix<T> & a, matrix<T> & b);
    };

    // Konstruktor - tworzy macierz ze strumienia cin
    //-----------------------------------------------
    template <class T> matrix<T>::matrix()
    {
        cin >> n >> m;   // Rozmiar macierzy
        s = n * m;
        A = new T [s]; // Rezerwujemy pamięć na macierz n x m

        for(int i = 0; i < s; i++) cin >> A[i]; // Wczytujemy wyrazy macierzy
    }

    // Konstruktor - tworzy pustą macierz na podstawie wymiarów
    //---------------------------------------------------------
    template <class T> matrix<T>::matrix(int r, int c)
    {
        n = r;
        m = c;
        s = n * m;
        A = new T [s]; // Rezerwujemy pamięć na macierz n x m
    }

    // Destruktor - usuwa macierz i zwraca zajętą przez nią pamięć
    //------------------------------------------------------------
    template <class T> matrix<T>::~matrix()
    {
        delete [] A;
    }

    // Zwraca wartość elementu
    // r - numer wiersza
    // c - numer kolumny
    //-------------------------
    template <class T> T matrix<T>::getv(int r, int c)
    {
        return A[r * m + c];
    }

    // Mnoży macierze
    //--------------------
    template <class T> void matrix<T>::multiply(matrix<T> & a, matrix<T> & b)
    {
         T sum;
         int i,j,k;

         for(i = 0; i < n; i++)
             for (j= 0; j < m; j++)
             {
                 sum = 0;
                 for(k = 0;k < a.m; k++) sum += a.getv(i,k) * b.getv(k,j);
                 A[i * m + j] = sum;
             }
    }
#endif // _matrix_class

// Program główny
//---------------

int main()
{
    // Wczytujemy dane dla macierzy

    setlocale(LC_ALL,"");

    cout << "Wpisz dane dla macierzy:" << endl << endl;

    matrix<int> A;
    matrix<int> B;

    // Ustawiamy macierz wyniku

    matrix<int> C(A.n,B.m);

    cout << endl
         << "Mnożenie macierzy" << endl
         << "-----------------" << endl << endl;
    cout << "Macierz A" << A.n << "x" << A.m << endl;

    for(int i = 0; i < A.n; i++)
    {
        for(int j = 0; j < A.m; j++) cout << setw(6) << A.getv(i,j);
        cout << endl;
    }
    cout << endl;

    cout << "Macierz B" << B.n << "x" << B.m << endl;

    for(int i = 0; i < B.n; i++)
    {
        for(int j = 0; j < B.m; j++) cout << setw(6) << B.getv(i,j);
        cout << endl;
    }
    cout << endl;

    // Mnożymy macierz A przez macierz B i wynik wpisujemy do macierzy C

    C.multiply(A,B);

    cout << "Wynik mnożenia w macierzy C" << C.n << "x" << C.m << endl;

    for(int i = 0; i < C.n; i++)
    {
        for(int j = 0; j < C.m; j++) cout << setw(6) << C.getv(i,j);
        cout << endl;
    }

    cout << endl;

    return 0;
}
Wynik
Wpisz dane dla macierzy:

3 4
0 2 4 6 8 10 12 14 16 18 20 22
4 5
1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39

Mnożenie macierzy
-----------------

Macierz A3x4
     0     2     4     6
     8    10    12    14
    16    18    20    22

Macierz B4x5
     1     3     5     7     9
    11    13    15    17    19
    21    23    25    27    29
    31    33    35    37    39

Wynik mnożenia w macierzy C3x5
   292   316   340   364   388
   804   892   980  1068  1156
  1316  1468  1620  1772  1924
Na początek:  podrozdziału   strony 

Zespół Przedmiotowy
Chemii-Fizyki-Informatyki

w I Liceum Ogólnokształcącym
im. Kazimierza Brodzińskiego
w Tarnowie
ul. Piłsudskiego 4
©2019 mgr Jerzy Wałaszek

Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone
pod warunkiem podania źródła oraz niepobierania za to pieniędzy.

Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl

Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.