Transponowanie macierzy Mnożenie macierzy Potęgowanie macierzy Wyznacznik macierzy

Podobne dokumenty
MACIERZE. Sobiesiak Łukasz Wilczyńska Małgorzata

Liczby całkowite i rzeczywiste

Uniwersytet Kazimierza Wielkiego w Bydgoszczy Zespół Szkół nr 5 Mistrzostwa Sportowego XV Liceum Ogólnokształcące w Bydgoszczy

(3 kwiecień 2014) Marika Pankowska Kamila Pietrzak

typ y y p y z łoż o on o e n - tab a lice c e w iel e owym m ar a o r we, e stru r kt k ury

Podstawy algorytmiki i programowania - wykład 2 Tablice dwuwymiarowe cd Funkcje rekurencyjne

Tablice. Monika Wrzosek (IM UG) Podstawy Programowania 96 / 119

r. Tablice podstawowe operacje na tablicach

Programowanie - wykład 4

O podstawowych operacjach na tablicach. Mateusz Ziółkowski, MBiU II

Anna Sobocińska Sylwia Piwońska

5. Rozwiązywanie układów równań liniowych

Macierze. Rozdział Działania na macierzach

Algorytmika i programowanie. Wykład 2 inż. Barbara Fryc Wyższa Szkoła Informatyki i Zarządzania w Rzeszowie

15. Macierze. Definicja Macierzy. Definicja Delty Kroneckera. Definicja Macierzy Kwadratowej. Definicja Macierzy Jednostkowej

Macierzowe algorytmy równoległe

Wykład 5. Metoda eliminacji Gaussa

Rozdział 5. Macierze. a 11 a a 1m a 21 a a 2m... a n1 a n2... a nm

ZASADY PROGRAMOWANIA KOMPUTERÓW ZAP zima 2015

Definicja macierzy Typy i właściwości macierzy Działania na macierzach Wyznacznik macierzy Macierz odwrotna Normy macierzy RACHUNEK MACIERZOWY

Metody i analiza danych

Programowanie i struktury danych

wagi cyfry pozycje

Sortowanie przez wstawianie Insertion Sort

Wstęp do Informatyki

Ekoenergetyka Matematyka 1. Wykład 3.

Wykład 6. Metoda eliminacji Gaussa: Eliminacja z wyborem częściowym Eliminacja z wyborem pełnym

Informacje wstępne #include <nazwa> - derektywa procesora umożliwiająca włączenie do programu pliku o podanej nazwie. Typy danych: char, signed char

Wymiar musi być wyrażeniem stałym typu całkowitego, tzn. takim, które może obliczyć kompilator. Przykłady:

Programowanie obiektowe W3

Zmienne i struktury dynamiczne

RACHUNEK MACIERZOWY. METODY OBLICZENIOWE Budownictwo, studia I stopnia, semestr 6. Instytut L-5, Wydział Inżynierii Lądowej, Politechnika Krakowska

WYKŁAD 3 (13 MARZEC 2014) LICZBY CAŁKOWITE I RZECZYWISTE. Bartosz Łakomy i Dariusz Dobiesz

Zajęcia nr 2 Programowanie strukturalne. dr inż. Łukasz Graczykowski mgr inż. Leszek Kosarzewski Wydział Fizyki Politechniki Warszawskiej

MACIERZE I WYZNACZNIKI

utworz tworzącą w pamięci dynamicznej tablicę dwuwymiarową liczb rzeczywistych, a następnie zerującą jej wszystkie elementy,

Programowanie dynamiczne

PODSTAWY AUTOMATYKI. MATLAB - komputerowe środowisko obliczeń naukowoinżynierskich - podstawowe operacje na liczbach i macierzach.

O MACIERZACH I UKŁADACH RÓWNAŃ

1 Macierze i wyznaczniki

PROBLEM. Znaleźć rozkład liczby p > 1. na iloczyn czynników pierwszych.

Luty 2001 Algorytmy (7) 2000/2001

Rozwiązanie. #include <cstdlib> #include <iostream> using namespace std;

; B = Wykonaj poniższe obliczenia: Mnożenia, transpozycje etc wykonuję programem i przepisuję wyniki. Mam nadzieję, że umiesz mnożyć macierze...

Zaawansowane algorytmy i struktury danych

Wprowadzenie do Scilab: macierze

n, m : int; S, a, b : double. Gdy wartości sumy składowej nie można obliczyć, to przyjąć Sij = 1.03 Dla obliczenia Sij zdefiniować funkcję.

Podstawy Programowania

Opis zagadnieo 1-3. Iteracja, rekurencja i ich realizacja

3. Macierze i Układy Równań Liniowych

Krótkie wprowadzenie do macierzy i wyznaczników

Wstęp do informatyki- wykład 12 Funkcje (przekazywanie parametrów przez wartość i zmienną)

Programowanie dynamiczne

Podstawy programowania skrót z wykładów:

1 Układy równań liniowych

Programowanie w C++ Wykład 2. Katarzyna Grzelak. 5 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 41

Wstęp do programowania

do instrukcja while (wyrażenie);

Część 4 życie programu

Wieczorowe Studia Licencjackie Wrocław, Wykład nr 6 (w oparciu o notatki K. Lorysia, z modyfikacjami) Sito Eratostenesa

Zestaw 12- Macierz odwrotna, układy równań liniowych

Tablice są typem pochodnym. Poniżej mamy przykłady deklaracji różnych tablic:

// Liczy srednie w wierszach i kolumnach tablicy "dwuwymiarowej" // Elementy tablicy są generowane losowo #include <stdio.h> #include <stdlib.

Wykład 7 Macierze i wyznaczniki

Analiza algorytmów zadania podstawowe

0 + 0 = 0, = 1, = 1, = 0.

Wskaźniki i dynamiczna alokacja pamięci. Spotkanie 4. Wskaźniki. Dynamiczna alokacja pamięci. Przykłady

1 Wskaźniki. 1.1 Główne zastosowania wskaźników

tablica: dane_liczbowe

Układy równań liniowych. Krzysztof Patan

Kontrola przebiegu programu

Struktura programu. Projekty złożone składają się zwykłe z różnych plików. Zawartość każdego pliku programista wyznacza zgodnie z jego przeznaczeniem.

2. Układy równań liniowych

5. Rekurencja. Przykłady

Podstawy algorytmiki i programowania - wykład 6 Sortowanie- algorytmy

Podstawy algorytmiki i programowania - wykład 1 Tablice powtórzenie Tablice znaków Tablice dwuwymiarowe

Rekurencja. Przygotowała: Agnieszka Reiter

Rozwiązywanie układów równań liniowych

Podstawy informatyki. Informatyka stosowana - studia niestacjonarne. Grzegorz Smyk. Wydział Inżynierii Metali i Informatyki Przemysłowej

Wstęp do programowania. Wykład 1

Uwaga: Funkcja zamień(a[j],a[j+s]) zamienia miejscami wartości A[j] oraz A[j+s].

Zadanie 2: Arytmetyka symboli

Wykład 1_2 Algorytmy sortowania tablic Sortowanie bąbelkowe

4. Napisz program wyznaczający wartość funkcji. f (x) = x cos x + e 4x 7. w zadanym punkcie.

Wykład 4. Informatyka Stosowana. Magdalena Alama-Bućko. 25 marca Magdalena Alama-Bućko Wykład 4 25 marca / 25

Podstawy Programowania Podstawowa składnia języka C++

Algorytmy i struktury danych. Wykład 4

Programowanie komputerowe. Zajęcia 3

Programowanie w C++ Wykład 2. Katarzyna Grzelak. 4 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 44

Treść wykładu. Układy równań i ich macierze. Rząd macierzy. Twierdzenie Kroneckera-Capellego.

Wstęp do programowania

1 Macierz odwrotna metoda operacji elementarnych

ZASADY PROGRAMOWANIA KOMPUTERÓW

Podstawowe elementy proceduralne w C++ Program i wyjście. Zmienne i arytmetyka. Wskaźniki i tablice. Testy i pętle. Funkcje.

Rozwiązywanie układów równań liniowych metody dokładne Materiały pomocnicze do ćwiczeń z metod numerycznych

Strategia "dziel i zwyciężaj"

Zapis liczb binarnych ze znakiem

Wykład II. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

1 P roste e t ypy p d a d n a ych c - c ąg ą g d a d l a szy 2 T y T py p z ł z o ł żo ż ne e d a d n a ych c : T BLICE

I. Podstawy języka C powtórka

Transkrypt:

Transponowanie macierzy Mnożenie macierzy Potęgowanie macierzy Wyznacznik macierzy

Problem Transponować macierz A m n na A T n m. Operacja transponowania macierzy polega na zamianie wierszy w kolumny i kolumn w wiersze. Operację transponowania oznaczamy literką T. Ponieważ kolumny i wiersze wymieniają się wzajemnie, macierz transponowana posiada n wierszy i m kolumn. Przykład: 3 6 3 1 7 2 4 2 0 3 3 1 = 3 1 4 3 6 7 2 3 3 2 0 1

Wejście m liczba wierszy macierzy, m N n liczba kolumn macierzy, n N A macierz do transponowania o m wierszach i n kolumnach, A R B macierz wynikowa o n wierszach i m kolumnach, B R Wyjście: Macierz B = A T Elementy pomocnicze: i w indeks wierszowy macierzy, i w N i k indeks kolumnowy macierzy, i k N Lista kroków: K01: Dla i w = 1,2,...,m wykonuj K02...K03 K02: Dla i k = 1,2,...,n wykonuj K03 K03: B[i k,i w ] A[i w,i k ] K04: Zakończ

Program generuje losowe liczby m i n (od 2 do 8). Następnie tworzy macierz A m n oraz macierz B n m. Macierz A jest wypełniana losowymi liczbami z zakresu od -99 do 99, wyświetlana i transponowana w macierzy B. Na koniec program wyświetla zawartość macierzy B.

#include <iostream> #include <iomanip> #include <cstdlib> #include <time.h> using namespace std; int main() int **A,**B,n,m,iw,ik; srand((unsigned)time(null)); // losujemy wymiary macierzy m = rand() % 7 + 2; n = rand() % 7 + 2; // tworzymy tablicę wskaźników A = new int * [m]; B = new int * [n]; // tworzymy tablice wierszy for(iw = 0; iw < m; iw++) A[iw] = new int[n]; for(iw = 0; iw < n; iw++) B[iw] = new int[m]; // wypełniamy macierz A losowymi liczbami for(iw = 0; iw < m; iw++) for(ik = 0; ik < n; ik++) A[iw][ik] = rand() % 199-99; // wyświetlamy macierz A cout << "m = " << m << endl << "n = " << n << endl << endl << "Matrix A:" << endl; for(iw = 0; iw < m; iw++) for(ik = 0; ik < n; ik++) cout << setw(5) << A[iw][ik]; cout << endl; // transponujemy macierz A w B for(iw = 0; iw < m; iw++) for(ik = 0; ik < n; ik++) B[ik][iw] = A[iw][ik]; // wyświetlamy macierz wynikową cout << endl << "Matrix B = AT:" << endl; for(iw = 0; iw < n; iw++) for(ik = 0; ik < m; ik++) cout << setw(5) << B[iw][ik]; cout << endl; // koniec, zwalniamy pamięć zajętą przez macierze for(iw = 0; iw < m; iw++) delete [] A[iw]; for(iw = 0; iw < n; iw++) delete [] B[iw]; delete [] A; delete [] B; return 0;

Problem: Transponować macierz kwadratową A n n w miejscu. Podany powyżej algorytm transponowania macierzy wymaga dodatkowej pamięci. Jeśli macierz jest kwadratowa, to możemy transponować ją w miejscu. zamieniając miejscami elementy kolumn i wierszy. Zasada jest następująca: Załóżmy, iż mamy transponować poniższą macierz stopnia 4: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Zwróć uwagę, iż dla macierzy kwadratowej główna przekątna pozostaje bez zmian po transponowaniu. Elementy leżące na głównej przekątnej nie musimy przemieszczać. W pierwszym kroku wymienimy zatem elementy pierwszej kolumny i pierwszego wiersza z pominięciem elementu 1, który leży właśnie na głównej przekątnej: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Po wykonaniu tej operacji pierwsza kolumna i pierwszy wiersz macierzy są gotowe. Nie będziemy ich już zmieniać. W drugim kroku wykonujemy identyczną operację, lecz na pozostałej części macierzy, tzn. z pominięciem pierwszego wiersza i pierwszej kolumny: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

W efekcie dwa pierwsze wiersze i dwie pierwsze kolumny macierzy są gotowe. Operację kontynuujemy z pozostałą częścią macierzy: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Operacja jest zakończona, ponieważ pozostała część macierzy jest już macierzą jednoelementową (zawiera tylko element 16), a transpozycja macierzy jednoelementowej jest tożsamościowa.

Wejście n stopień macierzy, n N A kwadratowa macierz do transponowania stopnia n, A R Wyjście: Macierz A = AT Elementy pomocnicze: i,j indeksy, i,j N t tymczasowe miejsce przechowywania elementu macierzy, t R Lista kroków: K01: Dla i = 1,2,...,n - 1 wykonuj K02...K05 K02: Dla j = i + 1,i + 2,...,n wykonuj K03...K05 K03: t A[i,j] K04: A[i,j] A[j,i] K05: A[j,i] t K06: Zakończ

Program generuje losowy stopień macierzy n (od 2 do 8). Następnie tworzy macierz kwadratową A n n, którą wypełnia losowymi liczbami z zakresu od -99 do 99. Macierz A zostaje wyświetlona i transponowana. Na koniec program wyświetla ponownie zawartość macierzy A.

#include <iostream> #include <iomanip> #include <cstdlib> #include <time.h> using namespace std; main() int **A,n,i,j,t; srand((unsigned)time(null)); // losujemy stopień macierzy n = rand() % 7 + 2; // tworzymy tablicę wskaźników A = new int * [n]; // tworzymy tablice wierszy A[i] = new int[n]; // wypełniamy macierz A losowymi liczbami for(j = 0; j < n; j++) A[i][j] = rand() % 199-99; cout << "n = " << n << endl << endl << "Matrix A:" << endl; for(j = 0; j < n; j++) cout << setw(5) << A[i][j]; cout << endl; // transponujemy macierz A for(i = 0; i < n - 1; i++) for(j = i + 1; j < n; j++) t = A[i][j]; A[i][j] = A[j][i]; A[j][i] = t; // wyświetlamy macierz A cout << endl << "Matrix AT:" << endl; for(j = 0; j < n; j++) cout << setw(5) << A[i][j]; cout << endl; // koniec, zwalniamy pamięć zajętą przez macierz delete [] A[i]; delete [] A; // wyświetlamy macierz A

Problem: Znaleźć wynik mnożenia macierzy A m n przez macierz B n p. Aby zrozumieć zasadę mnożenia dwóch macierzy, zastosujemy prosty schemat postępowania. Załóżmy, iż mamy macierz A 3 4 i B 4 5 o następującej zawartości: A 3 x 4 = 1 2 3 4 5 6 7 8 9 8 7 6 A 4 x5 = 4 3 2 1 2 3 4 5 6 7 8 9 8 7 6 5 4 3 2 1

Wynikiem mnożenia dwóch macierzy jest nowa macierz, która posiada tyle wierszy, ile wierszy miała macierz A oraz tyle kolumn, ile kolumn miała macierz B. W naszym przypadku macierz ta będzie posiadała rozmiar 3 5, ponieważ macierz A posiada 3 wiersze, a macierz B posiada 5 kolumn. Zatem: A m n B n p = C m p Oznaczmy tę macierz jako C 3 5. Po lewej stronie macierzy C umieszczamy macierz A, natomiast macierz B umieszczamy ponad macierzą C. Obliczamy element c 1,1 jako sumę iloczynów kolejnych elementów wiersza 1 macierzy A przez elementy kolumny 1 macierzy B: 4 3 2 1 2 3 4 5 6 7 8 9 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9 8 7 6 54?????????????? c 1,1 = (1 4) + (2 3) + (3 8) + (4 5) = 4 + 6 + 24 + 20 = 54

Wynika z tego fakt, iż macierz A musi posiadać w wierszu tyle samo elementów, co macierz B w kolumnie. Zatem rozmiary tych macierzy nie mogą być dowolne, lecz muszą spełniać prosty warunek: A m n, B n p Podobnie obliczamy element c 1,2 jako sumę iloczynów kolejnych elementów wiersza 1 macierzy A przez elementy kolumny 2 macierzy B: 1 2 3 4 5 6 7 8 9 8 7 6 4 3 2 1 2 3 4 5 6 7 8 9 8 7 6 5 4 3 2 1 54 54????????????? c 1,2 = (1 3) + (2 4) + (3 9) + (4 4) = 3 + 8 + 27 + 16 = 54

Operację tę kontynuujemy aż do wyliczenia wszystkich elementów w wierszu 1 macierzy C: 1 2 3 4 5 6 7 8 9 8 7 6 4 3 2 1 2 3 4 5 6 7 8 9 8 7 6 5 4 3 2 1 54 54 48 42 38?????????? c 1,3 = (1 2) + (2 5) + (3 8) + (4 3) = 2 + 10 + 24 + 12 = 48 c 1,4 = (1 1) + (2 6) + (3 7) + (4 2) = 1 + 12 + 21 + 8 = 42 c 1,5 = (1 2) + (2 7) + (3 6) + (4 1) = 2 + 14 + 18 + 4 = 38

Po wyliczeniu wiersza 1 macierzy C rozpoczynamy obliczanie elementów wiersza 2. Działania wykonujemy wg tego samego schematu sumujemy iloczyny kolejnych elementów wiersza macierzy A przez kolejne elementy kolumny macierzy B. 1 2 3 4 5 6 7 8 9 8 7 6 4 3 2 1 2 3 4 5 6 7 8 9 8 7 6 5 4 3 2 1 54 54 48 42 38 134 134 120 106 102????? c 2,1 = (5 4) + (6 3) + (7 8) + (8 5) = 20 + 18 + 56 + 40 = 134 c 2,2 = (5 3) + (6 4) + (7 9) + (8 4) = 15 + 24 + 63 + 32 = 134 c 2,3 = (5 2) + (6 5) + (7 8) + (8 3) = 10 + 30 + 56 + 24 = 120 c 2,4 = (5 1) + (6 6) + (7 7) + (8 2) = 5 + 36 + 49 + 16 = 106 c 2,5 = (5 2) + (6 7) + (7 6) + (8 1) = 10 + 42 + 42 + 8 = 102

Na koniec wyliczamy ostatni wiersz macierzy C według tego samego schematu postępowania: 4 3 2 1 2 3 4 5 6 7 8 9 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9 8 7 6 54 54 48 42 38 134 134 120 106 102 146 146 132 118 122 c 3,1 = (9 4) + (8 3) + (7 8) + (6 5) = 36 + 24 + 56 + 30 = 146 c 3,2 = (9 3) + (8 4) + (7 9) + (6 4) = 27 + 32 + 63 + 24 = 146 c 3,3 = (9 2) + (8 5) + (7 8) + (6 3) = 18 + 40 + 56 + 18 = 132 c 3,4 = (9 1) + (8 6) + (7 7) + (6 2) = 9 + 48 + 49 + 12 = 118 c 3,5 = (9 2) + (8 7) + (7 6) + (6 1) = 18 + 56 + 42 + 6 = 122

Rachunek skończony. Możemy zapisać: 1 2 3 4 5 6 7 8 9 8 7 6 Χ 4 3 2 1 2 3 4 5 6 7 8 9 8 7 6 5 4 3 2 1 = 54 54 48 42 38 134 134 120 106 102 146 146 132 118 122

Wejście m,n,p rozmiary macierzy, m,n,p N A macierz o rozmiarze m n, A R B macierz o rozmiarze n p, B R C macierz o rozmiarze m p, C R Wyjście: Macierz C = A B Elementy pomocnicze: i,j,k indeksy elementów macierzy, i,j,k N s suma częściowa, s R Lista kroków: K01: Dla i = 1,2,...,m wykonuj K02...K06 K02: Dla j = 1,2,...,p wykonuj K03...K06 K03: s 0 ; zerujemy sumę częściową K04: Dla k = 1,2,...,n wykonuj K05 ; obliczamy sumę iloczynów K05: s s + A[i,k] B[k,j] K06: C[i,j] s ; sumę umieszczamy w elemencie macierzy wynikowej K07: Zakończ

// Mnożenie macierzy // Data: 26.01.2010 // (C)2012 mgr Jerzy Wałaszek //----------------------------- #include <iostream> #include <iomanip> using namespace std; int main() int **A,**B,**C,m,n,p,i,j,k,s; // odczytujemy wymiary macierzy cin >> m >> n >> p; // tworzymy macierze o odpowiednich rozmiarach A = new int * [m]; B = new int * [n]; C = new int * [m]; for(i = 0; i < m; i++) A[i] = new int[n]; C[i] = new int[p]; B[i] = new int[p]; // odczytujemy dane dla macierzy A for(i = 0; i < m; i++) for(j = 0; j < n; j ++) cin >> A[i][j]; // odczytujemy dane dla macierzy B for(j = 0; j < p; j++) cin >> B[i][j]; cout << endl; // mnożymy macierz A przez B i wynik umieszczamy w C for(i = 0; i < m; i++) for(j = 0; j < p; j++) s = 0; for(k = 0; k < n; k++) s += A[i][k] * B[k][j]; C[i][j] = s; // wyprowadzamy wynik mnożenia w C cout << "C = A x B:\n"; for(i = 0; i < m; i++) for(j = 0; j < p; j++) cout << setw(6) << C[i][j]; cout << endl; // zwalniamy pamięć zajętą przez macierze for(i = 0; i < m; i++) delete [] A[i]; delete [] C[i]; delete [] B[i]; delete [] A; delete [] B; delete [] C; return 0;

Problem: Obliczyć k-tą potęgę A n n. Rozwiązanie nr 1: Zdefiniujmy następujące operacje: A 0 = I (macierz jednostkowa) A 1 = A A 2 = A A A 3 = A A A A k = A A... A (k - 1 mnożeń) Pierwszy algorytm będzie algorytmem naiwnym, który wykonuje k-1 mnożeń macierzy A.

Wejście n stopień macierzy, n N, n > 0 A potęgowana macierz, A R k wykładnik potęgowy, k N, k 0 Wyjście: A zawiera k-tą potęgę wejściowej macierzy A. Elementy pomocnicze: W macierz pomocnicza, przechowuje wyniki częściowe mnożenia, W R P macierz pomocnicza, przechowuje A, P R i do sterowania pętlą, i N Lista kroków: K01: Jeśli k > 0, to idź do K04 K02: Ustaw macierz jednostkową w A ;A0 = I K03: Zakończ K04: Jeśli k = 1, to zakończ ; A1 = A K05: P A ; zapamiętaj oryginalną macierz A K06: Dla i = 2,3,..., k wykonuj K07...K08 K07: W P A ; wykonaj mnożenie K08: A W ; w A wynik mnożenia (i potęgowania) K09: Zakończ

// Potęgowanie macierzy // Data: 9.02.2011 // (C)2012 mgr Jerzy Wałaszek //----------------------------- #include <iostream> #include <iomanip> using namespace std; // procedura mnożenia macierzy // C = A x B //---------------------------- void mnoz(int n, double ** A, double ** B, double ** C) int i,j,k; double s; for(j = 0; j < n; j++) s = 0; for(k = 0; k < n; k++) s += A[i][k] * B[k][j]; C[i][j] = s; // procedura przepisuje macierz A do macierzy B //--------------------------------------------- void przepisz(int n, double ** A, double ** B) int i,j; for(j = 0; j < n; j++) B[i][j] = A[i][j]; // procedura ustawia w macierzy A macierz jednostkową //--------------------------------------------------- void jednostkowa(int n, double ** A) int i,j; for(j = 0; j < n; j++) A[i][j] = 0; A[i][i] = 1; // procedura wylicza potęgę k-tą macierzy A //----------------------------------------- void potega(int k, int n, double ** A) double ** P, ** W; int i; if(!k) jednostkowa(n,a); else if(k > 1) // tworzymy macierze pomocnicze P i W P = new double * [n]; W = new double * [n]; P[i] = new double[n]; W[i] = new double[n]; // macierz A zapamiętujemy w P przepisz(n,a,p); // w pętli wykonujemy kolejne mnożenia - wynik zawsze w A for(i = 2; i <= k; i++) mnoz(n,p,a,w); // W <- P x A przepisz(n,w,a); // A <- P x A // usuwamy macierze P i W delete [] P[i]; delete [] W[i]; delete [] P; delete [] W; //*** PROGRAM GŁÓWNY *** //---------------------- int main() double ** A; int n,i,j,k; cout << fixed << setprecision(2); // wczytujemy wykładnik k oraz stopień macierzy n cin >> k >> n; // tworzymy macierz dynamiczną i wczytujemy dane wierszami A = new double * [n]; A[i] = new double[n]; for(j = 0; j < n; j++) cin >> A[i][j]; // obliczamy k-tą potęgę A potega(k,n,a); // wyświetlamy wyniki cout << endl; for(j = 0; j < n; j++) cout << setw(10) << A[i][j] << " "; cout << endl; // usuwamy macierz A delete [] A[i]; delete [] A; return 0;

Rozwiązanie nr 2: Zauważmy następującą własność mnożenia macierzy: An+m = An Am Dalej: A2 = A A A4 = A2 A2 A8 = A4 A4... Jak widzimy, potęgi o wykładnikach będących potęgami liczby 2 możemy łatwo otrzymywać mnożąc macierz przez siebie. Idea nowego algorytmu potęgowania przedstawię na prostym przykładzie. Załóżmy, iż chcemy obliczyć A13. Zatem k = 13. Możemy zapisać: A13 = A8+4+1 = A8 A4 A Zwróć uwagę, iż mnożymy przez siebie macierze, które są potęgami pierwotnej macierzy A o wykładnikach równych 8, 4 i 1. Na takie liczby rozkłada się 13. Takie potęgi bardzo łatwo otrzymać. Potrzebujemy 3 mnożeń do wyliczenia wszystkich niezbędnych potęg macierzy A. Aby ostatecznie otrzymać A13, potrzebne są jeszcze dwa dodatkowe mnożenia, czyli w sumie 5 mnożeń. Poprzedni algorytm doszedłby do tego samego wyniku po wykonaniu aż 12 mnożeń. Przy większych potęgach zysk jest jeszcze większy.

Wykładnika k wcale nie musimy rozbijać na potęgi liczby 2. W pamięci komputera k jest przechowywane w postaci binarnej. Wystarczy testować stan odpowiednich bitów k: kolejne potęgi liczby 2 = 2 4 2 3 2 2 2 1 2 0 bity k = 0 1 1 0 1 numery pozycji bitów kolejnych = 4 3 2 1 0 Zasada pracy algorytmu jest następująca: W macierzy wyniku W ustawiamy macierz jednostkową. Dopóki k jest większe od zera, testujemy najmłodszy bit k i jeśli ma on wartość 1, to wykonujemy mnożenie W = W A. Bity k przesuwamy o 1 w prawo, aby pozbyć się testowanego bitu. Jeśli po tej operacji k ma wartość 0, to potęgowanie jest zakończone i przerywamy pętlę. W przeciwnym razie macierz A podnosimy do kwadratu: A = A A i pętlę kontynuujemy. Gdy pętla się zakończy, przepisujemy macierz W do A.

Wejście n stopień macierzy, n N, n > 0 A potęgowana macierz, A R k wykładnik potęgowy, k N, k 0 Wyjście: A zawiera k-tą potęgę wejściowej macierzy A. Elementy pomocnicze: W macierz pomocnicza, tworzony jest w niej wynik potęgowania, W R P macierz pomocnicza, P R Lista kroków: K01: Ustaw macierz jednostkową w W K02: Dopóki k > 0, wykonuj K03...K09 ; w pętli obliczamy k-tą potęgę A K03: Jeśli k and 1 = 0, to idź do K06 ; testujemy kolejne bity k K04 P W A ; jeśli bit jest ustawiony, to do W dołączamy A K05: W P K06: k k shr 1 ; przesuwamy w prawo bity k K07: Jeśli k = 0, to idź do K10 K08: P A A ; wyznaczamy kolejny kwadrat A K09 A P K10: A W ; wynik do A K11: Zakończ

// procedura wylicza potęgę k-tą macierzy A //----------------------------------------- void potega(int k, int n, double ** A) double ** P, ** W; int i; // Szybkie potęgowanie macierzy // Data: 19.02.2012 // (C)2012 mgr Jerzy Wałaszek //----------------------------- #include <iostream> #include <iomanip> using namespace std; // procedura mnożenia macierzy // C = A x B //---------------------------- void mnoz(int n, double ** A, double ** B, double ** C) int i,j,k; double s; for(j = 0; j < n; j++) s = 0; for(k = 0; k < n; k++) s += A[i][k] * B[k][j]; C[i][j] = s; // procedura przepisuje macierz A do macierzy B //--------------------------------------------- void przepisz(int n, double ** A, double ** B) int i,j; for(j = 0; j < n; j++) B[i][j] = A[i][j]; // procedura ustawia w macierzy A macierz jednostkową //--------------------------------------------------- void jednostkowa(int n, double ** A) int i,j; for(j = 0; j < n; j++) A[i][j] = 0; A[i][i] = 1; // tworzymy macierze W i P W = new double * [n]; P = new double * [n]; W[i] = new double[n]; P[i] = new double[n]; // w macierzy W ustawiamy macierz jednostkową jednostkowa(n,w); // w pętli obliczamy potęgę macierzy A w W while(k) if(k & 1) // testujemy najmłodszy bit k mnoz(n,w,a,p); // jeśli ustawiony, to przepisz(n,p,w); // W = W x A k >>= 1; // przesuwamy bity k w prawo if(!k) break; // jeśli brak bitów 1, przerywamy mnoz(n,a,a,p); // A = A x A przepisz(n,p,a); // wynik potęgowania wraca do macierzy A przepisz(n,w,a); // usuwamy macierze W i P delete [] W[i]; delete [] P[i]; delete [] W; delete [] P; //*** PROGRAM GŁÓWNY *** //---------------------- int main() double ** A; int n,i,j,k; cout << fixed << setprecision(2); // wczytujemy wykładnik k oraz stopień macierzy n cin >> k >> n; // tworzymy macierz dynamiczną i wczytujemy dane wierszami A = new double * [n]; A[i] = new double[n]; for(j = 0; j < n; j++) cin >> A[i][j]; // obliczamy k-tą potęgę A potega(k,n,a); // wyświetlamy wyniki cout << endl; for(j = 0; j < n; j++) cout << setw(10) << A[i][j] << " "; cout << endl; // usuwamy macierz A delete [] A[i]; delete [] A; return 0;

Problem: Obliczyć wyznacznik macierzy kwadratowej A n n za pomocą rozwinięcia Laplace'a. Wyznacznik (ang. determinant oznaczany det) jest liczbą, która została skojarzona z daną macierzą kwadratową A. Rozwinięcie Laplace'a pozwala w sposób rekurencyjny policzyć wyznacznik dowolnej macierzy kwadratowej. Wzór jest następujący: A macierz kwadratowa o rozmiarze n, której wyznacznik liczymy i ustalony wiersz macierzy A, wg którego dokonujemy rozwinięcia Laplace'a j kolejne numery kolumn w macierzy A ai,j element macierzy A leżący w i-tym wierszu i j-tej kolumnie Mi,j minor elementu ai,j - jest to wyznacznik macierzy powstałej z A po usunięciu z niej i-tego wiersza i j-tej kolumny

Powyższy wzór jest rekurencyjny, ponieważ występuje w nim Minor, który sam jest wyznacznikiem, a zatem obliczamy go za pomocą tego samego wzoru. Dla przykładu obliczmy wyznacznik macierzy: Jako wiersz rozwinięcia wybierzemy pierwszy wiersz zawierający elementy 1, 2 i 3. Dla każdego z tych elementów musimy wyznaczyć minory. Najpierw wyznaczmy macierze minorowe:

Obliczamy wyznaczniki M. Rozwijamy je wg pierwszego wiersza. Minory macierzy o rozmiarze 2 są macierzami jednoelementowymi. Wyznacznik macierzy jednoelementowej jest równy temu elementowi: M1,1 = (-1)1+1 5 2 + (-1)1+2 4 7 M1,1 = (-1)2 10 + (-1)3 28 M1,1 = 10-28 M1,1 = -18 M1,2 = (-1)1+1 6 2 + (-1)1+2 4 3 M1,2 = (-1)2 12 + (-1)3 12 M1,2 = 12-12 M1,2 = 0 M1,3 = (-1)1+1 6 7 + (-1)1+2 5 3 M1,3 = (-1)2 42 + (-1)3 15 M1,3 = 42-15 M1,3 = 27

Mając policzone wartości minorów, możemy przystąpić do wyznaczenia wyznacznika macierzy A: det A = (-1) 1+1 1 M 1,1 + (-1) 1+2 2 M 1,2 + (-1) 1+3 3 M 1,3 det A = (-1) 2 M 1,1 + (-1) 3 2M 1,2 + (-1) 4 3M 1,3 det A = M 1,1-2M 1,2 + 3M 1,3 det A = -18-0 + 81 det A = 63 Możemy już przystąpić do wstępnego określenia algorytmu. Wyznacznik macierzy A n n obliczamy następująco: Jeśli n = 1, to wyznacznik macierzy jest równy wartości elementu tej macierzy, czyli: det A 1 1 = det [ a 1 ] = a 1

Dla n > 1 wybieramy dowolny wiersz lub kolumnę (najlepiej taki, w którym jest najwięcej zer). Następnie każdy wyraz tego wiersza lub kolumny przemnażamy przez wyznacznik macierzy, która powstaje przez usunięcie wiersza i kolumny z mnożonym wyrazem (wyznacznik ten obliczamy rekurencyjnie tą samą metodą). Jeśli suma numeru wiersza i kolumny mnożonego wyrazu jest nieparzysta, to otrzymany iloczyn mnożymy dodatkowo przez -1. Wyliczone iloczyny sumujemy otrzymując wartość wyznacznika. Klasa złożoności obliczeniowej podanej metody jest równa O(n!). Dokonajmy prostej analizy. Wyznacznik wyliczamy mnożąc kolejne wyrazy wiersza (lub kolumny) przez wartości wyznaczników niższego poziomu i sumując otrzymane iloczyny (dochodzi jeszcze ewentualna zmiana znaku). Zatem dla wyznacznika n-tego poziomu musimy wyliczyć n wyznaczników poziomu (n-1). Czyli: ilość mnożeń = n ilość mnożeń dla wyznacznika poziomu n - 1 ilość dodawań = n ilość dodawań dla wyznacznika poziomu n - 1 + (n - 1) dodawań iloczynów

W poniższej tabeli wyliczyliśmy ilości mnożeń i dodawań dla kilku kolejnych wartości n. Złożoność obliczeniowa operacji wyliczania wyznacznika macierzy n Ilość dodawań ilość mnożeń 1 d 1 = 0 m 1 = 0 2 d 2 = 1 = 2d 1 + 1 m 2 = 2 3 d 3 = 5 = 3d 2 + 2 m 3 = 6 = 3m 2 4 d 4 = 23 = 4d 3 + 3 m 4 = 24 = 4m 3 5 d 5 = 119 = 5d 4 + 4 m 5 = 120 = 5m 4 Widać wyraźnie, iż od wartości n = 2 ilość mnożeń jest równa n!, a ilość dodawań jest równa n! - 1. Prowadzi to do bardzo szybkiego wzrostu czasu wykonania algorytmu. Pozostaje drobna kwestia techniczna. W algorytmie obliczamy wyznaczniki macierzy, które powstają przez usunięcie z macierzy głównej wiersza i kolumny z elementem mnożącym. Aby uniknąć kopiowania elementów, do algorytmu wyliczającego wyznacznik będziemy przekazywali wektor numerów kolumn, w którym umieścimy kolejne numery kolumn zawarte w tej podmacierzy oraz numer pierwszego wiersza. Na przykład w poniższej macierzy chcemy wyliczyć wyznacznik wg elementu a1,3:

1 2 3 4 podmacierz wektor kolumn 1 a 1,1 a 1,2 a 1,3 1 2 4 a 1,4 2 a 2,1 a 2,2 a 1 2 4 2 a 2,1 a 2,2 a 2,3 2,4 a 2,4 3 a 3,1 a 3,2 a 3,4 3 a 3,1 a 3,2 a 3,3 numer wiersza = 2 a 3,4 4 a 4,1 a 4,2 a 4,4 4 a 4,1 a 4,2 a 4,3 a 4,4 Dane te jednoznacznie określą podmacierz, której wyznacznik należy wyliczyć. Zwróć uwagę, iż wektor kolumn zawiera tyle elementów, ile wynosi stopień wyznacznika. Numer wiersza zawsze będzie numerem o 1 większym od wiersza zawierającego mnożony przez wyznacznik element. Do elementów podmacierzy będziemy się odwoływać zawsze poprzez wektor kolumn.

Wejście n określa stopień wyznacznika do obliczenia. n N w określa numer wiersza, w którym rozpoczyna się podmacierz. w N WK wektor kolumn. Zawiera numery kolumn z macierzy głównej, które zawiera podmacierz. WK zawiera tyle numerów kolumn, ile wynosi stopień wyznacznika. Elementy N i są numerowane od 0. A macierz, której wyznacznik liczymy. Wiersze i kolumny są numerowane od zera. A R Wyjście: Wynik działania funkcji det(n,w,wk,a) jest wartością wyznacznika Elementy pomocnicze: i,j,k zmienne pomocnicze dla pętli i,j,k N m mnożnik iloczynu wyrazu macierzy przez wyznacznik podmacierzy. Przyjmuje na przemian wartości 1 oraz (-1), m C. KK wektor kolumn umożliwiający rekurencyjne przekazywanie numerów kolumn podmacierzy, dla których liczone są wyznaczniki. Elementami wektora kolumn są liczby całkowite. Elementy C i są numerowane od zera. Wektor KK ma o jeden element mniej niż wektor WK otrzymany na wejściu. s zlicza sumę iloczynów wyrazów wiersza przez wyznaczniki niższych stopni. s R

Lista kroków: Funkcja rekurencyjna det(n,w,wk,a): Parametrami funkcji są: n stopień podmacierzy przekazywane przez wartość w bieżący wiersz macierzy głównej, w którym rozpoczyna się podmacierz przekazywane przez wartość WK wektor kolumn o n elementach przekazanie przez referencję A macierz podstawowa przekazanie przez referencję K01: Jeśli n = 1, to zakończ z wynikiem A[w][WK[0]] ; sprawdzamy zakończenie rekurencji zwracamy wynik funkcji K02: Utwórz wektor KK o n-1 elementach ; tworzymy tablicę dynamiczną K03: s 0 ; przygotowujemy się do sumowania iloczynów wyrazów przez minory K04: m 1 ; mnożnik (-1)i+j K05: Dla i = 0,1,...,n - 1 wykonuj K06...K12 ; rozpoczynamy pętlę obliczającą rekurencyjnie rozwinięcie Laplace'a K06: k 0 ; przygotowujemy wektor kolumn dla wywołania rekurencyjnego K07: Dla j = 0,1,...,n - 2 wykonuj K08...K10 K08: Jeśli k = i, to k k + 1 ; pomijamy kolumnę z bieżącym wyrazem K09: KK[j] WK[k] ; przepisujemy kolumny z WK do KK pomijając bieżącą K10: k k + 1 K11: s s + m A[w][WK[i]] det(n - 1, w + 1, KK, A) ; wywołanie rekurencyjne K12: m (- m) ; następny mnożnik (-1)i+j K13: Usuń wektor KK ; tablica dynamiczna przestaje być potrzebna, zwalniamy pamięć K12: Zakończ z wynikiem s ; wynik funkcji

// Wyznacznik wg rekurencyjnego rozwinięcia Laplace'a // Data : 8.02.2011 // (C)2012 mgr Jerzy Wałaszek //---------------------------- #include <iostream> #include <iomanip> using namespace std; // Rekurencyjna funkcja obliczająca rozwinięcie Laplace'a //------------------------------------------------------- double det(int n, int w, int * WK, double ** A) int i,j,k,m, * KK; double s; if(n == 1) return A[w][WK[0]]; else // sprawdzamy warunek zakończenia rekurencji // macierz 1 x 1, wyznacznik równy elementowi //*** PROGRAM GŁÓWNY *** //---------------------- int main() int n,i,j; // stopień macierzy int * WK; // wektor kolumn double ** A; // macierz cout << fixed << setprecision(4); cin >> n; A = new double * [n]; A[i] = new double[n]; for(j = 0; j < n; j++) cin >> A[i][j]; // odczytujemy stopień macierzy // tworzymy macierz wskaźników // tworzymy wiersz // czytamy wiersz macierzy KK = new int[n - 1]; // tworzymy dynamiczny wektor kolumn WK = new int[n]; // tworzymy wiersz kolumn s = 0; m = 1; // zerujemy wartość rozwinięcia // początkowy mnożnik WK[i] = i; // wypełniamy go numerami kolumn // pętla obliczająca rozwinięcie k = 0; // tworzymy wektor kolumn dla rekurencji for(j = 0; j < n - 1; j++) // ma on o 1 kolumnę mniej niż WK if(k == i) k++; // pomijamy bieżącą kolumnę KK[j] = WK[k++]; // pozostałe kolumny przenosimy do KK s += m * A[w][WK[i]] * det(n - 1,w + 1, KK, A); cout << endl; cout << det(n, 0, WK, A) << endl; // obliczamy i wyświetlamy wyznacznik delete [] WK; // usuwamy tablice dynamiczne delete [] A[i]; delete [] A; m = -m; // kolejny mnożnik delete [] KK; return s; // usuwamy zbędną już tablicę dynamiczną // ustalamy wartość funkcji