NUMERYCZNE ALGORYTMY PRZECHOWYWANIA MACIERZY

Transkrypt

NUMERYCZNE ALGORYTMY PRZECHOWYWANIA MACIERZY
Scientific Bulletin of Chelm
Section of Mathematics and
Computer Science
No. 1/2008
NUMERYCZNE ALGORYTMY PRZECHOWYWANIA
MACIERZY RZADKICH
RADOSŁAW MATUSIK
Katedra Analizy Matematycznej i Teorii Sterowania,
Wydział Matematyki i Informatyki, Uniwersytet Łódzki
Streszczenie. W artykule przedstawiono różne typy macierzy rzadkich, a także wybrane numeryczne algorytmy przechowywania tych macierzy. Każdy z algorytmów
omówiony został na przykładzie. W artykule wskazano również obszary zastosowań
każdego ze schematów, a także korzyści wynikające z przechowywania elementów macierzy rzadkich za pomocą tych algorytmów.
1. Wstęp
Przez macierz rzadką będziemy rozumieć macierz, której tylko nieznaczna część elementów jest różna od zera. Macierze tego typu mają szerokie zastosowanie w praktyce,
m.in. w teorii sieci elektrycznych, teorii grafów, genetyce, czy też socjologii.
Istnieje wiele numerycznych algorytmów przechowywania w pamięci komputera macierzy rzadkich. Powstały one w celu zaoszczędzenia pamięci, a także ograniczenia liczby
wykonywanych na nich operacji. Pozwalają m.in. na rozwiązywanie układów równań liniowych przy mniejszej liczbie działań arytmetycznych, niż w przypadku układu o tej
samej liczbie równań z macierzą pełną, umożliwiają wyznaczenie rozwiązania z większą
precyzją obliczeń, a także pozwalają zaoszczędzić pamięć maszyny wykonującej obliczenia. Kilka algorytmów przechowywania macierzy rzadkich zostało omówionych w dalszej
części artykułu.
2. Przykłady macierzy rzadkich
Do macierzy rzadkich zaliczamy następujące typy macierzy o charakterystycznej strukturze:
a) diagonalne: elementy niezerowe znajdują się na głównej przekątnej
⎡
x
0
x
0
...
0 0
⎢0
⎢
⎢
0
A=⎢
⎢
⎣...
0
0
x
...
0
225
...
...
...
...
0
⎤
0
0⎥
⎥
0⎥
⎥,
⎥
...⎦
x
226
RADOSŁAW MATUSIK
b) trójkątne: elementy niezerowe znajdują się pod główną przekątną (macierz trójkątna dolna) lub nad główną przekątną (macierz trójkątna górna)
⎡
x
⎢x
⎢
⎢x
A=⎢
⎢
⎣...
x
0
x
x
...
x
0
0
x
...
x
...
...
...
...
x
⎤
⎡
0
0⎥
⎥
0⎥
⎥
⎥
...⎦
x
x
⎢0
⎢
A=⎢
⎢0
⎢
⎣...
0
lub
x
x
0
...
0
x
x
x
...
0
...
...
...
...
0
⎤
x
x⎥
⎥
x⎥
⎥,
⎥
...⎦
x
c) wstęgowe: αij = 0, jeśli i − j > β lub j − i > γ dla pewnych β i γ, αk,k−β = 0
dla chociaż jednej wartości k, αk,k+γ = 0 dla chociaż jednej wartości k. Szerokość wstęgi (czyli maksymalna liczba elementów niezerowych w wierszu) takiej
macierzy wynosi w = β + γ + 1. Macierz wstęgowa jest symetryczna, gdy γ = β,
αij = 0 dla i − j > 1 oraz j − i > 2
⎡
x
⎢x
⎢
⎢
⎢0
⎢
A=⎢
⎢0
⎢0
⎢
⎢
⎣0
0
x
x
x
0
0
0
0
x
x
x
x
0
0
0
0
x
x
x
x
0
0
0
0
x
x
x
x
0
0
0
0
x
x
x
x
⎤
0
0⎥
⎥
⎥
0⎥
⎥
0⎥
⎥.
x⎥
⎥
⎥
x⎦
x
Widać, że szerokość wstęgi wynosi w tym przypadku w = 1 + 2 + 1 = 4.
d) trójdiagonalne (szczególny przypadek macierzy wstęgowej, gdy β = γ = 1):
elementy niezerowe występują symetrycznie po obu stronach głównej przekątnej
⎡
x
⎢
⎢x
⎢
⎢0
A=⎢
⎢...
⎢
⎢
⎣0
0
x
x
x
...
0
0
0
x
x
...
0
0
0
0
x
...
0
0
0
0
0
...
0
0
...
...
...
...
...
...
0
0
0
...
x
0
0
0
0
...
x
x
⎤
0
0⎥
⎥
⎥
0⎥
⎥.
...⎥
⎥
x⎥
⎦
x
Do macierzy rzadkich zaliczamy nie tylko te o specjalnej strukturze, jak w powyższych
przykładach. Za macierz rzadką uznaje się każdą macierz, w której liczba elementów
zerowych zdecydowanie przewyższa liczbę elementów niezerowych, np.
⎡
⎤
0 0 x 0 x 0 0 0 0 0
0 0 0 0 0 0 0 0 0⎥
⎦.
x 0 0 0 0 0 x x 0 0
A=⎢
⎣0
3. Numeryczne algorytmy przechowywania macierzy rzadkich
Bardzo często macierze rzadkie mają duże rozmiary i przechowywanie wszystkich
elementów w tradycyjny sposób w pamięci komputera, tj. w postaci jednej tablicy, jest
nieefektywne. Dlatego stosuje się różne metody, których celem jest zoptymalizowanie
zapisu takich macierzy. W ogólnym przypadku algorytmy przechowywania macierzy
rzadkich nie zapamiętują położenia elementów zerowych.
NUMERYCZNE ALGORYTMY PRZECHOWYWANIA MACIERZY RZADKICH
a) Schemat diagonalny
Rozważmy następującą macierz:
⎡
1 0
2
3
4
0
0
0 0
⎢0
⎢
⎢
⎢0
⎢
A=⎢
⎢0
⎢0
⎢
⎢
⎣0
0
3
5
0
0
0
0
227
⎤
0 0 0 0
4 0 0 0⎥
⎥
⎥
0 0 0 0⎥
⎥
6 7 0 0⎥
⎥.
7 8 9 10⎥
⎥
⎥
0 9 11 0 ⎦
0 10 0 12
W przypadku schematu diagonalnego elementy niezerowe przechowywane są w tablicy
AN o wymiarze n × β + 1, gdzie n jest wymiarem macierzy wejściowej, natomiast
β = max βi jest tzw. półszerokością wstęgi, przy założeniu, że na lewo od głównej
i=1,2,...,n
przekątnej znajduje się element niezerowy. Otrzymujemy zatem: β1 = 0, β2 = 0, β3 = 1,
β4 = 2, β5 = 1, β6 = 1 oraz β7 = 2, stąd β = 2. Zatem macierz AN ma wymiar
n × β + 1 = 7 × 2 + 1 = 7 × 3 i przyjmuje postać:
⎡
⎤
1
2⎥
⎥
⎥
5⎥
⎥
6⎥
⎥.
8⎥
⎥
⎥
11⎦
12
⎢
⎢
⎢
⎢0
⎢
A=⎢
⎢4
⎢0
⎢
⎢
⎣0
0
3
0
7
9
10 0
b) Schemat powłokowy
Rozważmy ponownie macierz A z poprzedniego przykładu.
W przypadku schematu powłokowego tworzy się dwie jednowymiarowe tablice:
AN - w której przechowywane są wierszami wszystkie elementy niezerowe (ze względu
na symetrię macierzy w każdym wierszu zapamiętywane są elementy maksymalnie do
głównej przekątnej)
AN = [1 2 3 5 4 0 6 7 8 9 11 10 0 12] ,
IA - w której przechowywane są wskaźniki do elementów znajdujących się na głównej
przekątnej.
Zauważmy, że elementom znajdującym się na głównej przekątnej odpowiadają następujące pozycje w tablicy AN : 1 − 1, 2 − 2, 5 − 4, 6 − 7, 8 − 9, 11 − 11 oraz 12 − 14.
Stąd
!
"
IA = 1 2 4 7 9 11 14 .
c) Schemat współrzędnych
Rozważmy następującą macierz:
⎡
1
⎢0
⎢
A=⎢
⎣0
6
0
3
0
7
0
4
5
0
⎤
2
0⎥
⎥
⎥.
0⎦
8
228
RADOSŁAW MATUSIK
W przypadku schematu współrzędnych tworzy się trzy jednowymiarowe tablice:
AN - w której przechowywane są wierszami wszystkie elementy niezerowe
!
"
AN = 1 2 3 4 5 6 7 8 ,
IA - w której przechowywane są indeksy wierszy, w których znajdują się elementy
niezerowe
!
"
IN = 1 1 2 2 3 4 4 4 ,
JA - w której przechowywane są wierszami indeksy kolumn, w których znajdują się
elementy niezerowe
!
"
JA = 1 4 2 3 3 1 2 4 .
d) Schemat rozrzedzony wierszowy
Rozważmy następującą macierz:
⎡
⎤
0 0 1 2 0 0 0 3 0 0
⎥
A=⎢
⎣0 0 0 0 0 0 0 0 0 0 ⎦ .
0 0 0 0 0 4 0 5 0 0
W przypadku schematu rozrzedzonego wierszowego tworzy się trzy jednowymiarowe
tablice:
AN - w której przechowywane są wierszami wszystkie elementy niezerowe
!
"
AN = 1 2 3 4 5 ,
JA - w której przechowywane są wierszami wskaźniki do indeksów kolumnowych, pod
którymi znajdują się elementy niezerowe.
Zauważmy, że niezerowe elementy znajdują się kolejno w kolumnie trzeciej, czwartej,
ósmej (w pierwszym wierszu) oraz szóstej i ósmej (w wierszu trzecim). Stąd
!
"
JA = 3 4 8 6 8 ,
IA - w której przechowywane są wierszami wskaźniki do danych zawartych w tablicach
AN i JA wskazujące pozycje pierwszych elementów niezerowych w każdym wierszu
!
"
IA = 1 4 4 6 .
e) Kompresja Shermana
Rozważmy macierz:
⎡
1
⎢0
⎢
⎢
⎢0
⎢
⎢0
⎢
⎢0
A=⎢
⎢
⎢0
⎢
⎢0
⎢
⎢0
⎢
⎢
⎣0
0
⎤
0 11 0 0 0 12 0 0 13
2 0 0 0 14 0 0 15 0 ⎥
⎥
⎥
0 3 0 0 0 16 0 0 17⎥
⎥
0 0 4 0 0 0 0 18 0 ⎥
⎥
0 0 0 5 0 19 0 0 20⎥
⎥
⎥.
0 0 0 0 6 0 21 22 23⎥
⎥
0 0 0 0 0 7 24 25 0 ⎥
⎥
0 0 0 0 0 0 8 26 0 ⎥
⎥
⎥
0 0 0 0 0 0 0 9 27⎦
0 0 0 0 0 0 0 0 10
NUMERYCZNE ALGORYTMY PRZECHOWYWANIA MACIERZY RZADKICH
229
W przypadku kompresji Shermana tworzy się pięć jednowymiarowych tablic:
AD - w której przechowywane są elementy znajdujące się na głównej przekątnej
!
"
AD = 1 2 3 4 5 6 7 8 9 10 ,
AN - w której przechowywane są wierszami wszystkie elementy niezerowe macierzy
trójkątnej (oczywiście oprócz głównej przekątnej)
AN = [11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27] ,
IA - w której przechowywane są wskaźniki do danych zawartych w tablicach AD
i AN wskazujące pozycje pierwszych elementów niezerowych w każdym wierszu (jak
w przypadku schematu rozrzedzonego wierszowego).
Zauważmy, że wypełnianie tablicy rozpoczynamy od pierwszego elementu, znajdującego się na głównej przekątnej. W pierwszym wierszu mamy trzy elementy niezerowe,
stąd 1 + 3 = 4. W drugim wierszu mamy dwa elementy niezerowe, stąd 4 + 2 = 6.
W trzecim wierszu elementów niezerowych jest 2 stąd 6 + 2 = 8. Łatwo widać, że tablica
IA zawiera następujące wskaźniki:
IA = [1 4 6 8 9 11 14 16 17 18 18] ,
JA - w której przechowywane są indeksy kolumnowe do elementów niezerowych wykorzystujące powtarzalność struktury.
Zauważmy, że w pierwszym wierszu elementy niezerowe znajdują się w kolumnach 3,
7 i 10, w drugim wierszu w kolumnach 6 i 9. Wiersze trzeci, czwarty i piąty pomijamy ze
względu na powtarzalność struktury. W wierszu szóstym elementy niezerowe znajdują
się w kolumnach 8, 9 i 10, w wierszu siódmym w 8 i 9. Wiersz ósmy pomijamy także
ze względu na powtarzalność. W wierszu dziewiątym mamy jeden element niezerowy
znajdujący się w kolumnie 10. Stąd tablica JA przyjmuje postać:
JA = [3 7 10 6 9 8 9 10 8 9 10] ,
IJ - w której przechowywane są wskaźniki do elementów znajdujących się w tablicy
JA umożliwiające wybór indeksów kolumnowych.
!
"
IJ = 1 4 2 5 2 6 9 10 11 .
f) Schemat Knutha
Rozważmy macierz:
⎡
⎤
0 1 0 0
3 0 4⎥
⎥
⎥.
0 0 0⎦
0 6 0 7
W przypadku schematu Knutha tworzy się siedem jednowymiarowych tablic:
AN - w której wierszami przechowywane są kolejne niezerowe elementy
⎢2
A=⎢
⎢
⎣5
!
"
AN = 1 2 3 4 5 6 7 ,
I - w której przechowywane są indeksy wierszowe do elementów niezerowych.
Zauważmy, że w pierwszym wierszu znajduje się jeden element niezerowy, w drugim
trzy, w trzecim jeden i w wierszu czwartym dwa elementy. Stąd:
!
"
I= 1 2 2 2 3 4 4 ,
230
RADOSŁAW MATUSIK
J - w której przechowywane są (wierszami) indeksy kolumnowe do elementów niezerowych.
Zauważmy, że w pierwszym wierszu element niezerowy znajduje się w drugiej kolumnie, w drugim wierszu elementy niezerowe znajdują się w kolumnie pierwszej, drugiej
i czwartej, w trzecim wierszu w kolumnie pierwszej i w wierszu czwartym w kolumnie
drugiej i czwartej. Stąd:
!
"
J = 2 1 24 1 2 4 .
N R - w której przechowywane są informacje o następnym elemencie wiersza. Zero
oznacza, że w danym wierszu oprócz danego elementu niezerowego nie ma innych elementów niezerowych
!
"
NR = 0 3 4 0 0 7 0 ,
N C - w której przechowywane są informacje o następnym elemencie kolumny. Znaczenie ”0” jest analogiczne, jak w opisie tablicy N R
!
"
NC = 3 5 6 7 0 0 0 ,
JR - w której przechowywane są informacje o pierwszym niezerowym elemencie danego wiersza.
Zauważmy, że licząc od pierwszego elementu niezerowego, pierwsze elementy niezerowe
w danym wierszu zaczynają się od pozycji 2 (jeden w wierszu pierwszym plus jeden
w drugim), 5 (dwa wcześniejsze plus trzy w trzecim wierszu) i 6 (pięć wcześniejszych
plus jeden w wierszu czwartym). Stąd:
!
"
JR = 1 2 5 6 ,
JC - w której przechowywane są informacje o pierwszym niezerowym elemencie danej
kolumny.
Zauważmy, że pierwsze elementy niezerowe w poszczególnych kolumnach znajdują się
na pozycjach 2 (w pierwszej kolumnie), 1 (w drugiej kolumnie), 0 (w kolumnie trzeciej
nie ma elementów niezerowych) oraz 2 (w kolumnie czwartej). Stąd:
!
"
JC = 2 1 0 2 .
Niektóre z zaprezentowanych w niniejszym artykule algorytmów przechowywania macierzy rzadkich można zmodyfikować, np. pewną modyfikacją schematu Knutha jest
powszechnie znany algorytm KRM (Knutha-Rheinboldta-Mesztenyi). Różnica polega
jedynie na sposobie zapamiętywania indeksów w tablicach N R oraz N C. Również po
drobnych modyfikacjach rozrzedzony schemat wierszowy można przedstawić jako inny
algorytm - rozrzedzony schemat kolumnowy. Jednak różnice między tymi algorytmami
są na tyle niewielkie, że nie wymagają osobnego przedstawienia w niniejszym artykule.
4. Podsumowanie
W poprzednim rozdziale zaprezentowane zostały wybrane numeryczne algorytmy przechowywania macierzy rzadkich. Wybór algorytmu uzależniony jest od postaci samej macierzy. W przypadku macierzy symetrycznej, w której elementy niezerowe skupione są
wokół głównej przekątnej należy wykorzystać schemat diagonalny, bądź powłokowy. Aby
przechować elementy macierzy trójkątnej (dolnej, bądź górnej) należy zastosować kompresję Shermana. Bardziej uniwersalne są schematy: współrzędnych, rozrzedzony wierszowy oraz Knutha, które pozwalają na zapamiętanie macierzy rzadkich o dowolnym
NUMERYCZNE ALGORYTMY PRZECHOWYWANIA MACIERZY RZADKICH
231
umiejscowieniu elementów niezerowych. Schemat Knutha zalecany jest w przypadku,
gdy elementy danej macierzy wygenerowane zostały przez jakiś algorytm, tj. w przypadku, gdy nie jest znany ani wymiar macierzy, ani ilość elementów niezerowych.
Rozważmy rozrzedzony schemat wierszowy i macierz z podpunktu d):
⎡
⎤
0 0 1 2 0 0 0 3 0 0
0 0 0 0 0 0 0 0 0⎥
⎦.
0 0 0 0 0 4 0 5 0 0
⎢
A = ⎣0
Zakładamy, że na zapamiętanie elementu macierzy potrzeba 8 bajtów, natomiast na
zapamiętanie wskaźnika 4 bajty.
Do zapamiętania wszystkich elementów macierzy A potrzeba 240 bajtów (30 elementów, każdy po 8 bajtów).
Natomiast w rozrzedzonym schemacie wierszowym musimy zapamiętać: jedną tablicę
zawierającą 5 elementów niezerowych (każdy po 8 bajtów), jedną tablicę zawierającą
5 wskaźników (każdy po 4 bajty) oraz jedną tablicę zawierającą 4 wskaźniki (także każdy
po 4 bajty). Stąd, na zapamiętanie odpowiednich elementów i wskaźników w przypadku
rozrzedzonego schematu wierszowego potrzebujemy 5 · 8B + 5 · 4B + 4 · 4B = 76B.
Widać zatem, że zastosowanie specjalnego algorytmu do przechowywania elementów
macierzy rzadkiej pozwoliło zaoszczędzić 164 bajty w pamięci komputera.
Warto się zastanowić, czy w każdym przypadku do zapamiętania elementów macierzy
rzadkiej trzeba stosować wybrany algorytm, czy może w niektórych przypadkach taką
macierz należy przechować w zwykłej tablicy, zapamiętując wszystkie jej elementy (także
zerowe).
Rozważmy ponownie schemat Knutha i przykładową macierz (z podpunktu f):
⎡
0 1
3
0
0 6
⎢2
⎢
A=⎢
⎣5
0
0
0
0
⎤
0
4⎥
⎥
⎥.
0⎦
7
Do zapamiętania wszystkich elementów macierzy A potrzeba 128 bajtów (16 elementów, każdy po 8 bajtów).
W schemacie Knutha potrzebujemy zapamiętać: 7 elementów niezerowych, każdy
po 8 bajtów, cztery siedmioelementowe tablice zawierające wskaźniki (czyli na zapamiętanie jednego wskaźnika potrzebujemy 4 bajty) oraz dwie czteroelementowe tablice
(również na zapamiętanie jednego wskaźnika potrzebujemy 4 bajty). Stąd, aby przechować potrzebne elementy oraz odpowiednie wskaźniki w schemacie Knutha potrzebujemy
7 · 8B + 4 · 7 · 4B + 2 · 4 · 4B = 200B. Widać, że w tym przypadku zastosowanie schematu
Knutha nie jest korzystne, gdyż na zapamiętanie elementów niezerowych i odpowiednich
wskaźników potrzebujemy o 72 bajty więcej, niż w przypadku zapamiętania wszystkich
elementów macierzy A (także zerowych). Jest to spowodowane małym wymiarem macierzy oraz niewielką liczbą elementów zerowych.
Podsumowując, w przypadku macierzy o bardzo dużych wymiarach, zawierających
jednocześnie bardzo dużą liczbę elementów zerowych, należy stosować wybrany algorytm przechowywania macierzy rzadkiej (odpowiednio do typu macierzy). Pozwoli on
na zaoszczędzenie pamięci, a w przypadku wykonywania na macierzy dodatkowych operacji, ograniczy ich liczbę. Jednak, jak pokazuje powyższy przykład, nie zawsze korzystne
jest stosowanie specjalnego algorytmu do zapamiętania elementów macierzy rzadkiej.
232
RADOSŁAW MATUSIK
Bibliografia
[1] Bjoerck, Dahlquist G., Metody numeryczne, Państwowe Wydawnictwo Naukowe, Warszawa 1987.
[2] Fortuna Z., Macukow B., Wąsowski J., Metody numeryczne, Wydawnictwa Naukowo-Techniczne,
Warszawa 1982.
[3] Śmietański M., Wykłady z algorytmów numerycznych - skrypt, 2006.
NUMERICAL ALGORITHMS STORING SPARSE MATRICES
RADOSŁAW MATUSIK
Abstract. The subject of article concerns the various types of sparse matrices and selected numerical
algorithms keeping these matrices. Each of these algorithms was discussed on example. This article
pointed out areas of adoptions each of schemas and profits come off keeping elements of sparse matrices
by means of these algorithms.