Bölüm 5: Polinomlar ve Seyrek Matrisler
Giriş
Öğrenme Çıktıları
Polinom için katsayı dizisi ile terim dizisini bellek ve zaman açısından karşılaştırmak.
Sıralı iki terim dizisini padd ile O(n + m)'de toplamak ve analiz etmek.
Polinomları bağlı listeyle çarpıp aynı üslü terimleri birleştirmek.
Matrisi ⟨row, col, value⟩ üçlüleriyle tutup ne zaman kazandırdığını söylemek.
transpose ve fast_transpose'u izleyip O(cols·terms) ile O(cols + terms)'ü karşılaştırmak.
Seyrek matris çarpımını transpoz yardımıyla kurmak; CSR gösterimini tanımak.
Polinom ADT ve Gösterim · Horowitz 2.4
Polinom: ⟨üs, katsayı⟩ çiftlerinden oluşan küme
A(x) = anxn + … + a1x + a0; her terim aixi bir katsayı ve bir üstür. Derece: sıfır olmayan en büyük üs.
| İşlem | Anlamı |
|---|---|
Zero(), IsZero(p) | sıfır polinomu kur / sına |
Coef(p, e) | e üslü terimin katsayısı (yoksa 0) |
LeadExp(p) | en büyük üs |
Attach(p, c, e), Remove(p, e) | terim ekle / çıkar |
SingleMult(p, c, e) | p · c xe |
Add(p1, p2), Mult(p1, p2) | toplam, çarpım |
A(x) + B(x) = Σ (ai + bi)·xi: aynı üslü katsayılar toplanır.
A(x)·B(x) = Σ (aixi · Σ bjxj): üsler toplanır, katsayılar çarpılır.
Polinom ADT ve Gösterim
1. yöntem (kötü): tüm katsayıları tut
#define MAX_DEGREE 101 /* azami derece + 1 */ typedef struct { int degree; /* en büyük üs */ float coef[MAX_DEGREE]; /* coef[i]: x^i katsayısı */ } polynomial; polynomial P;
degree saklanınca derece her seferinde hesaplanmaz; Coef ve toplama çok basittir.
Katsayısı 0 olan terimler de tutulur: seyrek polinomda büyük bellek israfı.
Polinom ADT ve Gösterim
2. yöntem (daha iyi): yalnız sıfır olmayan terimler
#define MAX_TERMS 101 /* azami terim sayısı */ typedef struct { float coef; /* katsayı */ int expon; /* üs */ } polynomial; polynomial P[MAX_TERMS]; /* tüm polinomlar burada */ int avail = 0; /* ilk boş konum */
Terimler büyük üsten küçüğe sıralı tutulur; her polinom P içinde bir [start, finish] aralığıdır.
A(x) = 3x²⁰ + 2x⁵ + 4 · B(x) = x⁵ + 10x³ + 3x² + 1
Polinom Toplama
İki işaretçiyle birleştirerek toplamak
- Üsler eşit: katsayılar toplanır; toplam ≠ 0 ise sonuca eklenir; iki işaretçi de ilerler.
- A'nınki büyük: “A'daki üsse sahip bir terim B'de yoktur” — B'nin kalan terimleri daha küçüktür. A'nın terimi eklenir, yalnız A ilerler.
- B'ninki büyük: simetrik; B'nin terimi eklenir.
- Birinde terim kalmayınca diğerinin kalanı olduğu gibi kopyalanır.
#define COMPARE(x, y) \ ((x) < (y) ? -1 : (y) < (x) ? 1 : 0) void attach(float coef, int expon) { if (avail >= MAX_TERMS) { fprintf(stderr, "Dizi tasmasi!\n"); exit(1); } P[avail].coef = coef; P[avail++].expon = expon; } #define EK(k) attach(P[k].coef, P[k].expon)
attach(int coef, …) yazılırsa float katsayı int’e kırpılır (2.5 → 2). Parametre float olmalı.Polinom Toplama · Kodu adım adım çalıştır
padd: A + B terim terim
A = 4x³ + 2x² + 5 → P[0..2], B = 3x³ − 2x² + x → P[3..5]. Sonuç D, avail = 6'dan başlar: *sd = 6.
COMPARE(3, 3) = 0: üsler eşit.
c = 4 + (3) = 7.
c ≠ 0 → attach(7, 3): P[6] = 7x³.
İki işaretçi de ilerler.
COMPARE(2, 2) = 0: üsler eşit.
c = 2 + (-2) = 0.
c = 0 → terim sonuca eklenmez (x² birbirini götürdü).
İki işaretçi de ilerler.
COMPARE(0, 1) = -1: B'nin üssü büyük.
B'nin terimi (x) D'ye eklenir; yalnız sb ilerler.
sb = 6 > fb = 5 → B bitti, döngüden çıkılır.
A'da kalan 5 olduğu gibi kopyalanır.
*fd = 8. D(x) = P[6..8] = 7x³ + x + 5.
void padd(int sa,int fa,int sb,int fb,int*sd,int*fd){ float c; *sd = avail; /* D'nin başı */ while (sa <= fa && sb <= fb) switch (COMPARE(P[sa].expon, P[sb].expon)) { case -1: EK(sb); sb++; break; /* B büyük */ case 1: EK(sa); sa++; break; /* A büyük */ case 0: c = P[sa].coef + P[sb].coef; if (c) attach(c, P[sa].expon); /* 0: ekleme */ sa++; sb++; } for (; sa <= fa; sa++) EK(sa); /* A'da kalan */ for (; sb <= fb; sb++) EK(sb); /* B'de kalan */ *fd = avail - 1; }
Polinom Toplama
padd'in karmaşıklığı
- A'da n, B'de m terim: her döngü turunda en az bir işaretçi ilerler → en çok n + m tur.
- Kalan kopyalama döngüleri de toplamda n + m'yi aşmaz → O(n + m).
- En iyi durum: tüm üsler ortak (n = m); her tur case 0, iki işaretçi birlikte ilerler → Ω(n).
- Sıralı olmasaydı: her terim için sonuçta arama/araya ekleme (kaydırma) → O(n²) düzeyine çıkardı.
Sonuçlar hep avail'dan eklenir; artık polinomların yeri geri kazanılmaz. P dolunca sıkıştırma (compaction) gerekir — ya da bağlı liste.
Polinom Toplama · ödev
ÖDEV 2: pmult, n polinomu toplamak ve çarpmak
- İki polinomu (P dizisi gösterimiyle) çarpan
pmultyordamını yazın ve performans analizini yapın. - n tane polinomu nasıl toplardınız? Veri yapısını kurun.
- n tane polinomu nasıl çarpardınız? Veri yapısını kurun.
Çözüm
- A'nın her aixei terimi için
SingleMultile B çarpılıp (m terim) ara sonucapaddile eklenir. Ara sonuç en çok n·m terim → toplam O(n·(nm + m)) = O(n²m). Küçük polinom dış döngüye alınırsa O(min(n,m)²·max(n,m)). - Polinomlar P'de
start[k], finish[k]dizileriyle tutulur. Art arda padd: toplam N terimde en kötü O(n·N). Daha iyisi: n işaretçiyi aynı anda ilerletip her adımda en büyük üssü seçmek; bir öncelikli kuyruk (heap) ile O(N log n). - Soldan sağa çarpım ara sonuçları büyütür; ikişer ikişer (ağaç biçiminde) çarpmak dengeli boyutlarla çalışır. Ara sonuçlar için aynı
start/finishtabloları ya da bağlı listeler kullanılır.
Bağlı Liste ile Polinom · Horowitz 4.4
Polinomu bağlı listeyle tutmak
typedef struct pol_oge *pol_gosterge; typedef struct pol_oge { int katsayi; int us; pol_gosterge bag; } pol_oge;
- Yalnız sıfır olmayan terimler, üsse göre azalan sırada.
- Terim sayısı önceden bilinmez; ortak dizi,
availve sıkıştırma derdi yok. - Araya ekleme/silme işaretçi değiştirerek O(1) (yeri bulunduktan sonra).
a = 3x¹⁴ + 2x⁸ + 1
Liste padd'i de iki işaretçiyle yürür: O(n + m); sonuç düğümleri malloc ile gelir, iş bitince free edilmeli (Horowitz 4.4).
Bağlı Liste ile Polinom
Yardımcılar: YeniTerimEkle ve SonrakiTerimiSil
/* terim oluştur; Onceki varsa peşine ekle */ pol_gosterge YeniTerimEkle(int Us, int Katsayi, pol_gosterge Onceki) { pol_gosterge p = malloc(sizeof *p); if (!p) { printf("Bellek yetmiyor!\n"); exit(1); } p->us = Us; p->katsayi = Katsayi; if (Onceki) { /* araya ya da sona */ p->bag = Onceki->bag; /* önce yeni düğüm */ Onceki->bag = p; /* sonra önceki */ } else p->bag = NULL; /* tek başına */ return p; }
/* Onceki'den sonrakini sil */ void SonrakiTerimiSil( pol_gosterge Onceki) { pol_gosterge Silinecek = Onceki->bag; if (!Silinecek) return; Onceki->bag = Silinecek->bag; free(Silinecek); }
Adres önce Silinecek’de tutulur: bağ güncellendikten sonra free edilebilsin; “bellekteki kalıntı temizlenmelidir”.
Bağlı Liste ile Polinom
Liste olarak verilen iki polinomu çarpmak
pol_gosterge yerlestir(pol_gosterge r,
int us, int kat) {
while (r->bag && r->bag->us > us) r = r->bag;
if (!r->bag || r->bag->us < us)
YeniTerimEkle(us, kat, r); /* yeni üs */
else if ((r->bag->katsayi += kat) == 0)
SonrakiTerimiSil(r); /* 0 oldu */
return r;
}- A'nın her terimi B'nin her terimiyle çarpılır: üsler toplanır, katsayılar çarpılır.
- Sonuç listesi sıralı kalmalı; aynı üslü terim varsa katsayı güncellenir, 0 olursa terim silinir.
- baslangic (
bas): p'nin bir sonraki terimiyle çarpımlar, bir öncekinin q1 ile çarpımının önünden başlayamaz → arama oradan başlar. - Okunaklılık için iç döngü gövdesi
yerlestiryordamına ayrıldı.
Bağlı Liste ile Polinom · Kodu adım adım çalıştır
PolCarp: (x + 1)·(x − 1)
A = x + 1, B = x − 1 çarpılacak (beklenen x² − 1). Sonuç listesi boş.
p = x. r = bas = NULL (henüz liste yok).
us = 1 + 1 = 2, kat = 1·(1) = 1.
Liste boş → ilk düğüm x²; rBas = r = bas.
q ilk terimdi → bas = r = &(1,2): p'nin sonraki terimi buradan aramaya başlayacak.
us = 1 + 0 = 1, kat = 1·(-1) = -1.
yerlestir: r'den sonra 1 üslü terim yok → yeni düğüm −x r'nin peşine eklenir.
p = 1. r = bas: arama baştan değil, &(1,2) düğümünden başlar.
us = 0 + 1 = 1, kat = 1·(1) = 1.
yerlestir: 1 üslü terim zaten var; katsayı −1 + 1 = 0 → SonrakiTerimiSil(r) ile silinir.
q ilk terimdi → bas = r = &(1,2): p'nin sonraki terimi buradan aramaya başlayacak.
us = 0 + 0 = 0, kat = 1·(-1) = -1.
yerlestir: r'den sonra 0 üslü terim yok → yeni düğüm −1 r'nin peşine eklenir.
Sonuç: x² − 1. n·m = 4 çarpım yapıldı; aynı üslü −x ve +x birleşip silindi.
pol_gosterge PolCarp(pol_gosterge pBas,
pol_gosterge qBas) {
pol_gosterge p, q, r = NULL, bas = NULL, rBas=NULL;
for (p = pBas; p; p = p->bag) {
r = bas; /* aramaya buradan başla */
for (q = qBas; q; q = q->bag) {
int us = p->us + q->us;
int kat = p->katsayi * q->katsayi;
if (rBas) r = yerlestir(r, us, kat);
else rBas=r=bas=YeniTerimEkle(us, kat, NULL);
if (q == qBas) bas = r; /* sonraki p için */
}
}
return rBas;
}Bağlı Liste ile Polinom
Problemin diğer çözümleri (A: n, B: m terim)
| yöntem | karmaşıklık | |
|---|---|---|
| 1 | Hepsi çarpılır, her gelen sona eklenir; sonra sıralanır ve birleştirilir | O(n²m²) |
| 2 | Hepsi çarpılır; her seferinde baştan taranıp uygun konuma eklenir | O(n²m²) |
| 3 | A'nın her terimiyle çarpım için bir liste kurulur, her adımda sonuçla birleştirilir (merge) | O(n²m) |
| 4 | 3. yöntem, küçük polinom dışta | O(min(n,m)²·max(n,m)) |
| 5 | A'nın her terimi için ayrı liste; en sonda hepsi (ikişer ikişer) birleştirilir | O(m·n·lg n) |
| 6 | 5. yöntem, küçük polinom dışta | O(m·n·lg min(m,n)) |
| 7 | Dizi açılır, n. indis n. üssü tutar | — |
Seyrek Matris
Seyrek matris: öğelerin çoğu sıfır
36 hücreden yalnız 8'i dolu
Kesin tanımı zordur; 2B dizide tutmak çok sayıda sıfır için bellek kaybıdır. Çözüm: her dolu öğeyi ⟨row, col, value⟩ üçlüsüyle tutmak.
#define MAX_TERMS 101 typedef struct { int row, col, value; /* satır, sütun, değer */ } term; term M[MAX_TERMS];
M[0] başlıktır: satır sayısı, sütun sayısı, dolu öğe sayısı. Üçlüler önce satıra, sonra sütuna göre sıralıdır.
Seyrek Matris
Üçlü tablo ve ne zaman kazandırır?
rows·cols tamsayı yerine 3·(t + 1). t ≈ rows·cols/3 olunca eşitlenir; yani matrisin 3'te 1'i dolu olduğunda bellek maliyeti aynıdır. Daha doluysa bu yapı tercih edilmez.
36 tamsayı yerine 3·9 = 27 tamsayı.
Create(max_row, max_col), Transpose(a), Add(a, b), Multiply(a, b) — Horowitz ADT 2.3. Başka gösterimler de var (bağlı listeli, CSR…).
Transpoz Alma
Transpoz: M[i][j] → M′[j][i]
Satırları sütun olarak yazarız:
- Üçlülerde basitçe row ile col'u değiştirmek yetmez: sonuç satır sırasını bozar.
- Değiştirip sonra sıralamak ya da her öğeyi yerine kaydırarak eklemek pahalıdır.
- Çözüm: transpozun satırları = M'nin sütunları; sütun sütun taranıp sırayla üretilir.
transpose iç döngüsünü j = 0’dan başlatmak başlık M[0]’ı da tarar. (M[0].col = sütun sayısı hiçbir i’ye eşit olmadığından sonuç bozulmuyor, ama gereksiz.) Doğrusu j = 1.Transpoz Alma · Kodu adım adım çalıştır
transpose: her sütun için tüm terimleri tara
Başlık: T 4×3 olur (satır ↔ sütun), yine 4 terim.
Sütun 0: M[1].col = 0 eşit → T[1] = (0, 0, 7), cur = 2.
Sütun 0: M[2].col = 3 ≠ 0 → atla.
Sütun 0: M[3].col = 1 ≠ 0 → atla.
Sütun 0: M[4].col = 0 eşit → T[2] = (0, 2, 2), cur = 3.
Sütun 1: M[1].col = 0 ≠ 1 → atla.
Sütun 1: M[2].col = 3 ≠ 1 → atla.
Sütun 1: M[3].col = 1 eşit → T[3] = (1, 1, 4), cur = 4.
Sütun 1: M[4].col = 0 ≠ 1 → atla.
Sütun 2: M[1].col = 0 ≠ 2 → atla.
Sütun 2: M[2].col = 3 ≠ 2 → atla.
Sütun 2: M[3].col = 1 ≠ 2 → atla.
Sütun 2: M[4].col = 0 ≠ 2 → atla.
Sütun 3: M[1].col = 0 ≠ 3 → atla.
Sütun 3: M[2].col = 3 eşit → T[4] = (3, 0, 3), cur = 5.
Sütun 3: M[3].col = 1 ≠ 3 → atla.
Sütun 3: M[4].col = 0 ≠ 3 → atla.
Bitti: T satır sırasına göre kendiliğinden sıralı (M'de satırlar sıralıydı). 4 sütun × 4 terim = 16 karşılaştırma.
void transpose(term M[], term T[]) { int n = M[0].value, i, j, cur; T[0].row = M[0].col; T[0].col = M[0].row; T[0].value = n; if (n > 0) { cur = 1; /* T'de sıradaki */ for (i = 0; i < M[0].col; i++) /* her sütun */ for (j = 1; j <= n; j++) /* her terim */ if (M[j].col == i) { T[cur].row = M[j].col; T[cur].col = M[j].row; T[cur].value = M[j].value; cur++; } } }
Transpoz Alma
transpose'un karmaşıklığı
- Dış döngü cols kez, iç döngü terms kez → O(cols · terms).
- Matris doluysa terms = rows·cols → O(cols² · rows): 2B dizide transpoz O(rows·cols) iken daha kötü!
- Yani bellekten kazanırken zamandan kaybettik — terms ≪ rows·cols olduğunda anlamlı.
- Sorun: aynı M dizisini her sütun için yeniden taramak.
Her terimin T'de nereye gideceğini önceden bilseydik, M'yi bir kez tarayıp her terimi doğrudan yerine koyardık.
Transpozun 0. satırında 2, 1. satırında 3 öğe olacaksa: 0. satır T[1]'den, 1. satır T[3]'ten, 2. satır T[6]'dan başlar.
Transpoz Alma
fast_transpose: önce sayma, sonra yerleştirme
void fast_transpose(term M[], term T[]) { int i, j, nc = M[0].col, nt = M[0].value; int row_terms[MAX_COL], start_pos[MAX_COL]; T[0].row = nc; T[0].col = M[0].row; T[0].value = nt; if (nt <= 0) return; /* boş matris */ for (i = 0; i < nc; i++) row_terms[i] = 0; for (i = 1; i <= nt; i++) row_terms[M[i].col]++; start_pos[0] = 1; for (i = 1; i < nc; i++) start_pos[i] = start_pos[i-1] + row_terms[i-1]; for (i = 1; i <= nt; i++) { j = start_pos[M[i].col]++; /* yerini al, artır */ T[j].row = M[i].col; T[j].col = M[i].row; T[j].value = M[i].value; } }
6×6 örnekte row_terms = [2, 1, 2, 2, 0, 1], start_pos = [1, 3, 4, 6, 8, 8].
Transpoz Alma · Kodu adım adım çalıştır
fast_transpose (1): say ve başlangıçları bul
Başlık kopyalanır: T 4×3, 4 terim. nt > 0 → devam.
row_terms sıfırlanır: T'nin her satırı (= M'nin her sütunu) için sayaç.
M[1] sütun 0'da → row_terms[0]++ = 1.
M[2] sütun 3'te → row_terms[3]++ = 1.
M[3] sütun 1'de → row_terms[1]++ = 1.
M[4] sütun 0'da → row_terms[0]++ = 2.
0. sütunun terimleri T[1]'den başlayacak: start_pos[0] = 1.
start_pos[1] = 1 + 2 = 3.
start_pos[2] = 3 + 1 = 4.
start_pos[3] = 4 + 0 = 4 (2. sütun boş: 3. sütun da 4'ten başlar).
void fast_transpose(term M[], term T[]) { int i, j, nc = M[0].col, nt = M[0].value; int row_terms[MAX_COL], start_pos[MAX_COL]; T[0].row = nc; T[0].col = M[0].row; T[0].value = nt; if (nt <= 0) return; /* boş */ for (i = 0; i < nc; i++) row_terms[i] = 0; for (i = 1; i <= nt; i++) row_terms[M[i].col]++; start_pos[0] = 1; for (i = 1; i < nc; i++) start_pos[i] = start_pos[i-1] + row_terms[i-1]; /* 2. aşama: terimleri yerine koy */
Transpoz Alma · Kodu adım adım çalıştır
fast_transpose (2): terimleri yerine koy
M bir kez, baştan sona taranır; her terim doğrudan yerine gider.
M[1] = (0, 0, 7): j = start_pos[0]++ → j = 1, start_pos[0] = 2.
T[1] = (0, 0, 7): satır/sütun yer değiştirdi.
M[2] = (0, 3, 3): j = start_pos[3]++ → j = 4, start_pos[3] = 5.
T[4] = (3, 0, 3): satır/sütun yer değiştirdi.
M[3] = (1, 1, 4): j = start_pos[1]++ → j = 3, start_pos[1] = 4.
T[3] = (1, 1, 4): satır/sütun yer değiştirdi.
M[4] = (2, 0, 2): j = start_pos[0]++ → j = 2, start_pos[0] = 3.
T[2] = (0, 2, 2): satır/sütun yer değiştirdi.
Bitti — sonuç transpose ile aynı. İki doğrusal tarama: O(cols + terms).
/* 1. aşama: row_terms ve start_pos hazır */ for (i = 1; i <= nt; i++) { j = start_pos[M[i].col]++; /* oku, artır */ T[j].row = M[i].col; T[j].col = M[i].row; T[j].value = M[i].value; } }
Transpoz Alma
Analiz ve tek dizili sürüm
Döngüler: num_cols + num_terms + num_cols + num_terms (+ sabitler):
2·terms + 2·cols + 3 = O(cols + terms)
Dolu matriste O(rows·cols): 2B dizideki transpozla aynı derece. Bedeli: iki yardımcı dizi (O(cols) bellek).
/* row_terms'ü yerinde start_pos'a çevir */ for (i = 0, pos = 1; i < nc; i++) { int c = cnt[i]; /* bu sütundaki terim sayısı */ cnt[i] = pos; /* artık başlangıç konumu */ pos += c; } for (i = 1; i <= nt; i++) { j = cnt[M[i].col]++; T[j].row = M[i].col; T[j].col = M[i].row; T[j].value = M[i].value; }
Hızlı transpoz tek bir dizi kullanılarak da yazılabilir. Bu, sayarak sıralamanın (counting sort) ta kendisi: anahtar = sütun no.
Seyrek Matris Çarpımı · Horowitz 2.5
Dm×p = Am×n · Bn×p
dij = Σk=0n−1 aik · bkj
A'nın i. satırı ile B'nin j. sütunu “çarpılıp toplanır”. İki seyrek matrisin çarpımı seyrek olmak zorunda değil:
- Üçlülerde A'nın i. satırı ardışık durur, ama B'nin j. sütunu dağınıktır.
- Bu yüzden önce B'nin transpozu alınır (fast_transpose): B'nin sütunları Bᵀ'nin ardışık satırları olur.
- Sonra A'nın her satırı ile Bᵀ'nin her satırı, col alanına göre birleştirilerek (merge) çarpılır.
mmult’ında (ve mantık olarak) transpozu alınan ikinci matris B’dir.Seyrek Matris Çarpımı · Horowitz Program 2.9 uyarlaması
Yardımcılar: satır sonu, nokta çarpım, sakla
/* s'deki terimin satırının bittiği yer */ int son(term t[], int s) { int e = s; while (e <= t[0].value && t[e].row == t[s].row) e++; return e; } /* a[x..ex) satırı · b[y..ey) satırı */ int carp(term a[], int x, int ex, term b[], int y, int ey) { int sum = 0; while (x < ex && y < ey) if (a[x].col < b[y].col) x++; else if (a[x].col > b[y].col) y++; else sum += a[x++].value * b[y++].value; return sum; }
void sakla(term d[], int *nd, int r, int c, int s) { if (s == 0) return; if (++*nd >= MAX_TERMS) { fprintf(stderr, "Cok terim\n"); exit(1); } d[*nd].row = r; d[*nd].col = c; d[*nd].value = s; }
carp, padd gibi iki işaretçili birleştirmedir: yalnız aynı k (col) değerli terimler çarpılır. Sıfır toplam saklanmaz.
Seyrek Matris Çarpımı · Kodu adım adım çalıştır
Seyrek çarpım: A'nın satırı · Bᵀ'nin satırı
A = [[1,0,2],[0,3,0]], B = [[4,0],[0,5],[6,0]]; B'nin transpozu bt önceden alındı.
A'nın 0. satırı: a[1..2].
B'nin 0. sütunu = bt[1..2].
carp: 1·4 + 2·6 = 16 → d[1] = (0, 0, 16).
B'nin 1. sütunu = bt[3..3].
carp: ortak col yok = 0 → sıfır, saklanmaz.
A'nın 1. satırı: a[3..3].
B'nin 0. sütunu = bt[1..2].
carp: ortak col yok = 0 → sıfır, saklanmaz.
B'nin 1. sütunu = bt[3..3].
carp: 3·5 = 15 → d[2] = (1, 1, 15).
Başlık yazılır: D 2×2, 2 terim. D = [[16, 0], [0, 15]].
void smult(term a[], term bt[], term d[]) { int ra, ea, rb, eb, nd = 0; for (ra = 1; ra <= a[0].value; ra = ea) { ea = son(a, ra); /* A satırı [ra,ea) */ for (rb = 1; rb <= bt[0].value; rb = eb) { eb = son(bt, rb); /* B sütunu [rb,eb) */ sakla(d, &nd, a[ra].row, bt[rb].row, carp(a, ra, ea, bt, rb, eb)); } } d[0].row = a[0].row; d[0].col = bt[0].row; d[0].value = nd; }
Seyrek Matris Çarpımı · Horowitz 2.5
Seyrek çarpımın karmaşıklığı
- Transpoz: O(colsB + tB).
- A'nın her satırı i için Bᵀ baştan sona gezilir: Σi tB = rowsA·tB.
- Her (i, j) çifti için A satırının terimleri de gezilir: Σj tA = colsB·tA.
- Toplam: O(colsB·tA + rowsA·tB).
O(rowsA·colsA·colsB). Matrisler doluysa (tA = rowsA·colsA) seyrek sürüm de aynı dereceye çıkar, sabiti daha büyüktür.
tA ≪ rows·cols ve tB ≪ rows·cols iken. Sonuç D'nin seyrek olacağı garanti değildir: MAX_TERMS aşılabilir.
Diğer Gösterimler
Sıkıştırılmış satır (CSR) gösterimi
Aynı 6×6 matris: 8 + 8 + 7 = 23 tamsayı (üçlülerde 27). i. satır: val[row_ptr[i] .. row_ptr[i+1]−1]; boş satır 3 → art arda eşit iki row_ptr.
/* y = M * x (matris-vektör çarpımı) */ for (i = 0; i < rows; i++) { y[i] = 0; for (k = row_ptr[i]; k < row_ptr[i+1]; k++) y[i] += val[k] * x[col_ind[k]]; }
Bilimsel hesapta en yaygın biçimlerden; M·x O(rows + terms). Sütun sürümü: CSC.
row_ptr, fast_transpose'taki start_pos ile aynı fikir (önek toplamları).
Diğer Gösterimler
Seyrek matrisin bağlı listeyle gösterimi
- Üçlü dizide ekleme/silme kaydırma ister ve MAX_TERMS sınırı vardır.
- Bağlı gösterimde her dolu öğe bir düğümdür; aynı satırdakiler ve aynı sütundakiler döngülü listelerle bağlanır; satır/sütun başlık düğümleri vardır (Horowitz 4.7).
- Ekleme/silme O(1) bağ değişikliği (yer bulunduktan sonra); satıra ve sütuna göre gezinme kolay.
- Bedeli: öğe başına iki işaretçi ek bellek.
Seyrek matrisin bağlı gerçeklemesi, bağlı listelerin doğrudan bir uygulamasıdır.
Kapanış
Karmaşıklık Özeti
| İşlem | Zaman | Not |
|---|---|---|
| padd (n, m terim) | O(n + m) | sıralı terimler + birleştirme |
| pmult (dizi, art arda padd) | O(n²m) | küçük polinom dışta: O(min²·max) |
| PolCarp (bağlı liste) | O(n²m) en kötü | bas ile arama kısalır |
| transpose | O(cols·terms) | dolu matriste O(cols²·rows) |
| fast_transpose | O(cols + terms) | O(cols) ek bellek |
| Seyrek çarpım (smult / mmult) | O(colsB·tA + rowsA·tB) | önce Bᵀ |
| CSR ile M·x | O(rows + terms) | satır başı dizisi |
Kapanış
Sık Yapılan Hatalar
attach'ta float katsayıyı int parametreyle kırpmak; avail taşmasını denetlememek.start_pos[M[i].col]++ ifadesinde ön ek/son ek farkını karıştırmak.Kapanış
Alıştırmalar
- A = 2x⁴ + 3x² − 1 ve B = −3x² + x + 1 için padd'i izleyin. D'de kaç terim olur?
- P dizisi gösteriminde 10 terimli iki polinomun toplamı en çok kaç attach çağrısı yapar?
- 5×5 matriste kaç sıfırdan fazla öğe olunca üçlü gösterim 2B diziden fazla yer kaplar?
- Terimleri (0,1,5), (1,0,2), (1,3,4), (2,1,1) olan 3×4 matrisin row_terms ve start_pos dizileri nedir?
- fast_transpose tek diziyle nasıl yazılır? Hangi sıralama algoritmasına benzer?
- (x² + 1)(x² − 1) çarpımında PolCarp hangi terimi siler?
İpucu / cevaplar
- D = 2x⁴ + x (3x² − 3x² = 0 ve −1 + 1 = 0 eklenmez): 2 terim.
- Hiç ortak üs yoksa 20.
- 3(t + 1) > 25 → t ≥ 8: 8 ve üstü dolu öğede.
- Sütunlar 1, 0, 3, 1 → row_terms = [1, 2, 0, 1], start_pos = [1, 2, 4, 4].
- row_terms'ü yerinde başlangıç konumlarına çevirip aynı diziyi artırarak; sayarak sıralama (counting sort).
- x² · (−1) = −x² ile 1 · x² = +x² toplanıp 0 olur: x² terimi silinir → x⁴ − 1.
Kapanış
Bölüm Özeti
Katsayı dizisi seyrek polinomda israf; terim dizisi yalnız anlamlı terimleri tutar.
padd O(n + m): sıralı iki diziyi iki işaretçiyle yürümek.
Terim sayısı bilinmiyorsa esnek; PolCarp aynı üsleri birleştirir, sıfırları siler.
⟨row, col, value⟩ + başlık M[0]; t < rows·cols/3 iken kazançlı.
transpose O(cols·t); fast_transpose say–yerleştir ile O(cols + t).
Önce Bᵀ, sonra satır·satır birleştirme; CSR aynı “satır başı” fikrinin standart biçimi.
Kapanış
Kaynaklar ve Devam
- E. Horowitz, S. Sahni, S. Anderson-Freed, Fundamentals of Data Structures in C, 2. bs., 2.4–2.5 (polinom, seyrek matris), 4.4 ve 4.7 (bağlı polinom ve seyrek matris).
- Y. Saad, Iterative Methods for Sparse Linear Systems, 3.4 (CSR/CSC biçimleri).