Ders 05 / 22
Temel Doğrusal Cebir İşlemleri
Matris çarpımında soyutlamanın işlem sayısını değiştirmediği ölçülür: 11.340 çarpma iki yolda da aynı kalır, çağrı yerindeki satır 13'ten 2'ye iner ve 26.460 elle dizin hesabı ortadan kalkar. Çözümde kazanç bir algoritma seçimidir: tanımdan yazılan kofaktör açılımı n = 12 için 823.059.744 işlem ister, eleme 704 işlemle biter, oran 1.169.119 kattır. Gizlenen karar pivotlamadır — işlem sayısı 14'te sabit kalırken pivotsuz eleme ilk katsayı küçüldükçe artığı 8,538e-11'den 7,237e+01'e taşır ve katsayı sıfırlandığında hiç çözüm üretmez. Kötü koşullanmış bir tarife sisteminde girdideki tek bir 0,01 m³ değişiklik ilk basamağın birim fiyatını yüzde 50,09 oynatır.
İçindekiler
Önceki ders iki diziyi öğe öğe eşleştiren işlemleri ele aldı: yayılım, aynı konumdaki iki değeri buluşturur. Tarife hesabının kendisi böyle çalışmaz. Bir abonenin üç dönemlik okuması üç tarife basamağına dağıtıldığında her sonuç değeri bir satırın tamamı ile bir sütunun tamamının birlikte hesabıdır. Bu ders bu iki işlemi — matris çarpımını ve ondan türeyen çözüm problemini — ölçer.
Ölçünün konusu cebir değildir; kuram burada anlatılmaz. Ölçülen şey, aynı sonucu üreten iki yazımın kaç işlem tuttuğu ve soyutlamayı çağırmanın hangi kararı üstlendiğidir. Bu konunun en keskin sayıları buradan çıkıyor, çünkü çözüm probleminde soyutlamanın kazancı satır sayısı değil, algoritma seçimidir.
- SH9. Matris çarpımında soyutlama işlem sayısını değiştirmez. Değiştirdiği şey çağrı yerindeki satır sayısı ve elle yazılan dizin hesabının ortadan kalkmasıdır.
- SH10. Çözümde soyutlamayı çağırmak bir algoritma seçmektir. Tanımdan yazılan yol ile elemenin işlem sayısı aynı büyüklük düzeyinde değildir ve fark boyutla birlikte hızla büyür.
- SH11. Aynı işlem sayısıyla iki farklı sonuç alınabilir. Pivotlama kararı artığın büyüklüğünü belirler; girdi duyarlılığı ise hesaptan bağımsızdır ve kötü koşullanmış bir sistemde ölçüm çözünürlüğü kadar bir fark sonucu yarı yarıya oynatır.
Aynı İşlem, Başka Yazım
# carpim.py — MODELDIR: matris carpimi once elle uc dongu ile, sonra tek cagri # olarak yaziliyor. Okuma tablosu (1260, 3), tarife matrisi (3, 3); sonuc her # abonenin uc tarife basamagindaki payi. Kume ve tarife kurgudur. import inspect def uretec(tohum): s = tohum & 0xFFFFFFFF def sonraki(): nonlocal s s = (1664525 * s + 1013904223) & 0xFFFFFFFF return s / 4294967296 return sonraki ABONE, DONEM = 1260, 3 r = uretec(20260218) HAM = [round(4 + r() * 56, 2) for _ in range(ABONE * DONEM)] r2 = uretec(9110723) p = [r2() for _ in range(ABONE * DONEM)] BOS = set(sorted(range(len(p)), key=lambda j: p[j])[:581]) OKUMA = [0.0 if j in BOS else HAM[j] for j in range(ABONE * DONEM)] # Tarife matrisi: her donemin okumasinin uc basamaga dagilim orani (10/25/40 m3). TARIFE = [0.62, 0.28, 0.10, 0.55, 0.31, 0.14, 0.48, 0.34, 0.18] ISLEM = 0 # bir carpma ve bir toplama DIZIN = 0 # elle yazilan dizin hesabi sayisi def carp_elle(A, na, ma, B, mb, nb): global ISLEM, DIZIN C = [0.0] * (na * nb) for i in range(na): for j in range(nb): t = 0.0 for k in range(ma): t += A[i * ma + k] * B[k * nb + j] ISLEM += 1 DIZIN += 2 C[i * nb + j] = t DIZIN += 1 return C def carp(A, ba, B, bb): return carp_elle(A, ba[0], ba[1], B, bb[0], bb[1]) ISLEM, DIZIN = 0, 0 C1 = carp_elle(OKUMA, ABONE, DONEM, TARIFE, DONEM, DONEM) i1, d1 = ISLEM, DIZIN ISLEM, DIZIN = 0, 0 C2 = carp(OKUMA, (ABONE, DONEM), TARIFE, (DONEM, DONEM)) i2, d2 = ISLEM, DIZIN satir = lambda f: len(inspect.getsource(f).strip().splitlines()) gen = lambda w: lambda h: "".join(str(v).ljust(w[0]) if j == 0 else str(v).rjust(w[j]) for j, v in enumerate(h)) y = gen([28, 16, 16, 10]) sapan = sum(1 for a, b in zip(C1, C2) if abs(a - b) > 1e-9) print(f"(1260, 3) x (3, 3) -> (1260, 3); sonuc {len(C1)} oge, {sapan} oge 1e-9 esiginin " f"ustunde sapiyor\n") print(y(["olcu", "elle uc dongu", "tek cagri", "oran"])) print(y(["cagri yerinde satir", satir(carp_elle), satir(carp), f"{satir(carp_elle)/satir(carp):.1f}x"])) print(y(["carpma-toplama sayisi", i1, i2, f"{i1/i2:.1f}x"])) print(y(["elle dizin hesabi", d1, 0, "-"])) print(f"\ncarpma sayisi {ABONE} x {DONEM} x {DONEM} = {ABONE*DONEM*DONEM}; soyutlama bu sayiyi " f"degistirmez, yalnizca {d1} dizin hesabini cagri yerinden kaldirir")
(1260, 3) x (3, 3) -> (1260, 3); sonuc 3780 oge, 0 oge 1e-9 esiginin ustunde sapiyor olcu elle uc dongu tek cagri oran cagri yerinde satir 13 2 6.5x carpma-toplama sayisi 11340 11340 1.0x elle dizin hesabi 26460 0 - carpma sayisi 1260 x 3 x 3 = 11340; soyutlama bu sayiyi degistirmez, yalnizca 26460 dizin hesabini cagri yerinden kaldirir
Matris çarpımında soyutlamanın kazancı ölçüldüğünde ortaya çıkan sayı bir uyarıdır. Çarpma sayısı
iki yazımda da 11.340 — bu sayı biçimlerden çıkar, yazımdan değil, ve hiçbir çağrı onu
düşürmez. Değişen tek şey nerede yazıldığıdır: çağrı yerindeki satır 13’ten 2’ye iner ve 26.460
elle dizin hesabı ortadan kalkar. A[i * ma + k] ile B[k * nb + j] ifadelerinin her biri düz
belleği iki boyutlu okumanın elle yazılmış halidir ve bu iki ifadenin birinde çarpanları yer
değiştirmek hiçbir hata üretmez, yalnız başka bir sayı üretir.
Kazancın gerçek adı budur: soyutlama hesabı hızlandırmaz, yanlış yazılabilecek yer sayısını düşürür. Ölçek büyüdüğünde bu değişmez; çarpma sayısı biçimlerin çarpımıdır ve bir kütüphane onu ancak farklı bir algoritma seçerek değiştirebilir. Bir sonraki bölüm tam olarak bunun yapıldığı yeri gösteriyor.
Soyutlamanın Seçtiği Algoritma
# cozum.py — MODELDIR: ayni denklem takimi iki yolla cozuluyor. Birincisi tanimdan # yazilir (determinant kofaktor acilimiyla), ikincisi elemeyle. Islem sayaci ikisini # de sayiyor; kutuphane cagrilmiyor, iki algoritma da acikca yaziliyor. KOF = 0 ELE = 0 def det_kofaktor(M, n): global KOF if n == 1: return M[0] t = 0.0 for j in range(n): alt = [M[i*n+k] for i in range(1, n) for k in range(n) if k != j] KOF += 1 t += (1 if j % 2 == 0 else -1) * M[j] * det_kofaktor(alt, n - 1) return t def coz_eleme(M, b, n, pivotla=True): global ELE A = [list(M[i*n:(i+1)*n]) + [b[i]] for i in range(n)] for k in range(n): if pivotla: en = max(range(k, n), key=lambda i: abs(A[i][k])) A[k], A[en] = A[en], A[k] for i in range(k + 1, n): c = A[i][k] / A[k][k] for j in range(k, n + 1): A[i][j] -= c * A[k][j] ELE += 1 x = [0.0] * n for i in range(n - 1, -1, -1): t = A[i][n] for j in range(i + 1, n): t -= A[i][j] * x[j] ELE += 1 x[i] = t / A[i][i] return x kofaktor_islem = lambda n: 0 if n <= 1 else n * (1 + kofaktor_islem(n - 1)) rk = uretec(70117) def kare(n): M = [round(1 + rk() * 9, 3) for _ in range(n * n)] b = [round(1 + rk() * 9, 3) for _ in range(n)] return M, b w = gen([6, 18, 14, 14, 12]) print(w(["n", "kofaktor islemi", "formul", "eleme islemi", "oran"])) for n in (2, 3, 4, 5, 6, 7, 8): M, b = kare(n) KOF, ELE = 0, 0 det_kofaktor(M, n) k = KOF coz_eleme(M, b, n) print(w([n, k, kofaktor_islem(n), ELE, f"{k/max(ELE,1):.0f}x"])) for n in (12,): M, b = kare(n) ELE = 0 x = coz_eleme(M, b, n) art = max(abs(sum(M[i*n+j] * x[j] for j in range(n)) - b[i]) for i in range(n)) print(w([n, "-", kofaktor_islem(n), ELE, f"{kofaktor_islem(n)/ELE:.0f}x"])) print(f"\nn = {n} icin kofaktor acilimi {kofaktor_islem(n)} islem ister; eleme {ELE} islemle " f"bitirir, oran {kofaktor_islem(n)/ELE:.0f} kat") print(f"elemenin cozumu denklemi sagliyor: en buyuk artik {art:.2e}, " f"esik 1e-9 (katsayilar 1e1 buyuklugunde, 64 bit kesirli tipin cozunurlugu 1e-16)")
n kofaktor islemi formul eleme islemi oran 2 2 2 4 0x 3 9 9 14 1x 4 40 40 32 1x 5 205 205 60 3x 6 1236 1236 100 12x 7 8659 8659 154 56x 8 69280 69280 224 309x 12 - 823059744 704 1169119x n = 12 icin kofaktor acilimi 823059744 islem ister; eleme 704 islemle bitirir, oran 1169119 kat elemenin cozumu denklemi sagliyor: en buyuk artik 2.66e-15, esik 1e-9 (katsayilar 1e1 buyuklugunde, 64 bit kesirli tipin cozunurlugu 1e-16)
Soru şudur: üç tarife basamağının birim fiyatı bilinmiyor, ama birkaç abone grubunun toplam faturası ve her grubun basamaklardaki tüketimi biliniyor. Bu bir denklem takımıdır ve iki yolla çözülür. Tanımdan yazılan yol determinantı kofaktör açılımıyla hesaplar; eleme yolu satırları birbirinden çıkararak bilinmeyenleri teker teker yalıtır.
Tablo iki yolun aynı büyüklük düzeyinde olmadığını gösteriyor. n = 4’e kadar fark yoktur, hatta eleme daha fazla iş yapar. n = 6’da oran 12 kat, n = 8’de 309 kattır. Ölçülen sayılar formülle birebir tutuyor — kofaktör açılımının işlem sayısı özyinelemesini izler ve bu, n arttıkça çarpanın kendisinin de arttığı bir büyümedir. n = 12 için 823.059.744 işlem eder; eleme aynı sistemi 704 işlemle bitirir. Oran 1.169.119 kat.
Ölçünün kursa taşınan biçimi budur: bir çözüm işlevini çağırmak bir kısayol değil, bir algoritma seçimidir. Kazanç satır sayısında değil, işlem sayısının büyüme biçimindedir. Aynı nedenle, hangi algoritmanın seçildiğini bilmeden çağrılan bir çözüm işlevi, hangi işlem sayısına razı olunduğunu bilmeden verilmiş bir karardır.
Sayısal Duyarlılık
# duyarlilik.py — MODELDIR: uc abone grubunun toplam faturasindan uc tarife # basamaginin birim fiyati cozuluyor. Katsayilar her grubun basamaklardaki m3 # miktari; kurgudur. Ilk iki grubun tuketim profili birbirine cok yakin. carpMV = lambda M, x, n: [sum(M[i*n+j] * x[j] for j in range(n)) for i in range(n)] GERCEK = [12.50, 18.75, 27.40] # birim fiyat, kurgu KAT = [10.00, 15.00, 5.00, 10.02, 15.04, 5.01, 8.00, 20.00, 12.00] FATURA = carpMV(KAT, GERCEK, 3) x0 = coz_eleme(KAT, FATURA, 3) KAT2 = list(KAT) KAT2[0] = KAT[0] + 0.01 # sayac cozunurlugu kadar tek bir degisiklik x1 = coz_eleme(KAT2, FATURA, 3) u = gen([16, 16, 18, 14]) print(f"katsayi matrisinin determinanti {det_kofaktor(KAT, 3):.4f}\n") print(u(["basamak", "cozulen fiyat", "0,01 m3 sonrasi", "sapma yuzde"])) for i, ad in enumerate(["10 m3", "25 m3", "40 m3"]): print(u([ad, f"{x0[i]:.6f}", f"{x1[i]:.6f}", f"{100*abs(x1[i]-GERCEK[i])/GERCEK[i]:.2f}"])) print(f"\ngirdideki tek bir 0,01 m3 degisiklik ilk basamagin fiyatini yuzde " f"{100*abs(x1[0]-GERCEK[0])/GERCEK[0]:.2f} oynatiyor; hesapta hicbir hata yok") # Pivotlama karari. Ilk katsayi kuculdukce pivotsuz eleme bozuluyor; deger burada # gercek bir okuma degil, algoritmanin davranisini olcen bir sondadir. v = gen([18, 18, 18, 14]) print("\n" + v(["ilk katsayi", "pivotlu artik", "pivotsuz artik", "islem sayisi"])) for eps in (1e-2, 1e-6, 1e-10, 1e-14, 0.0): T = [eps, 15.00, 5.00, 10.02, 15.04, 5.01, 8.00, 20.00, 12.00] bt = carpMV(T, GERCEK, 3) artik = lambda xx: max(abs(sum(T[i*3+j] * xx[j] for j in range(3)) - bt[i]) for i in range(3)) ELE = 0 xp = coz_eleme(T, bt, 3, True) n_islem = ELE try: xn = coz_eleme(T, bt, 3, False) pa = f"{artik(xn):.3e}" except ZeroDivisionError: pa = "cozum yok" print(v([f"{eps:g}", f"{artik(xp):.3e}", pa, n_islem])) print("\npivotlama islem sayisini degistirmiyor; degistirdigi sey artigin buyuklugu")
katsayi matrisinin determinanti 0.8000 basamak cozulen fiyat 0,01 m3 sonrasi sapma yuzde 10 m3 12.500000 6.239082 50.09 25 m3 18.750000 25.001560 33.34 40 m3 27.400000 21.154679 22.79 girdideki tek bir 0,01 m3 degisiklik ilk basamagin fiyatini yuzde 50.09 oynatiyor; hesapta hicbir hata yok ilk katsayi pivotlu artik pivotsuz artik islem sayisi 0.01 0.000e+00 8.538e-11 14 1e-06 0.000e+00 5.687e-07 14 1e-10 0.000e+00 4.120e-03 14 1e-14 0.000e+00 7.237e+01 14 0 0.000e+00 cozum yok 14 pivotlama islem sayisini degistirmiyor; degistirdigi sey artigin buyuklugu
Üst tablo hesabın dışındaki bir duyarlılığı ölçüyor. İlk iki abone grubunun tüketim profili birbirine çok yakın; katsayı matrisinin determinantı 0,8 gibi küçük bir sayı. Bu sistemde girdiye sayaç çözünürlüğü kadar, yani 0,01 m³ eklenince ilk basamağın birim fiyatı 12,50’den 6,24’e düşer, yüzde 50,09 sapma. Diğer iki basamak da yüzde 33 ve yüzde 23 oynar. Hesapta hiçbir hata yoktur; her iki sonuç da kendi girdisinin doğru çözümüdür. Oynayan şey girdinin kendisidir ve girdi zaten ölçümle geldiği için 0,01 m³’lük belirsizliği taşır.
Bu, konunun en önemli sınırıdır: iki ölçüm arasındaki fark sayaçtan okunamayacak kadar küçükken sonuç yarı yarıya değişiyorsa, o sonuç raporlanabilir bir sayı değildir. Bir çözüm işlevinin verdiği değer doğru olabilir ve aynı zamanda anlamsız olabilir; ikisini ayıran şey girdiyi oynatıp sonucun ne kadar hareket ettiğine bakmaktır.
Alt tablo ise soyutlamanın gizlediği kararı gösteriyor. Pivotlama, elemenin her adımında en büyük katsayıyı öne almaktır ve işlem sayısını hiç değiştirmez: beş satırın hepsinde 14. Değiştirdiği şey artıktır. İlk katsayı 0,01 iken pivotsuz elemenin artığı 8,538e-11 ile eşiğin altındadır ve iki yol ayırt edilemez. Katsayı küçüldükçe artık büyür: 1e-10’da 4,120e-03, 1e-14’te 7,237e+01 — yani sonuç artık denklemi hiç sağlamaz. Katsayı sıfır olduğunda pivotsuz eleme çözüm bile üretemez, pivotlu eleme aynı 14 işlemle doğru sonucu verir. Bir çözüm işlevini çağırırken pivotlamanın açık olup olmadığı yazılmıyorsa, artığın büyüklüğü hakkında hiçbir şey iddia edilemez.
Özet
- Matris çarpımında soyutlama işlem sayısını değiştirmez: 11.340 çarpma iki yazımda da aynıdır; çağrı yerindeki satır 13’ten 2’ye iner ve 26.460 elle dizin hesabı ortadan kalkar.
- Çözümde kazanç bir algoritma seçimidir: kofaktör açılımı ile eleme n = 4’e kadar denk, n = 8’de 309 kat, n = 12’de 823.059.744’e karşı 704 işlemle 1.169.119 kat ayrışır.
- Elemenin çözümü denklemi sağlar: n = 12’de en büyük artık 2,66e-15 ve 1e-9 eşiğinin çok altındadır.
- Pivotlama işlem sayısını değiştirmez (14’te sabit), artığı değiştirir: ilk katsayı küçüldükçe pivotsuz artık 8,538e-11’den 7,237e+01’e çıkar, katsayı sıfırken çözüm hiç üretilmez.
- Kötü koşullanmış bir tarife sisteminde (determinant 0,8) girdiye sayaç çözünürlüğü kadar 0,01 m³ eklenince birim fiyatlar yüzde 50,09, 33,34 ve 22,79 sapar; hesapta hata yoktur.
Sonraki Adım
Beş derste sayısal hesabın yapısı kuruldu ve her adımda iki sayı yan yana durdu. Ortam sabitlendiğinde defter 17 adımın 17’sinde aynı sonucu verdi, dizi arayüzü aynı işi 22 satır yerine 2 satırda ve 188.864 bayt yerine 30.320 baytta yaptı, vektörleştirme satırı beşte bire indirirken geçişi üçe katladı, yayılım kurulmayan diziyle 30.320 bayt kazandırırken uyumsuz bir biçimde 1.586.340 öğeyi sessizce yanlış hesapladı ve çözüm işlevi 1.169.119 katlık bir işlem farkını tek bir çağrının arkasına aldı.
Sayılar hızlandı, ama bu beş dersin bütün kod bloklarında tekrarlanan bir şey var: hangi sütunun
ne olduğunu söyleyen tek şey koddur. OKUMA[i * DONEM + d] ifadesi ikinci boyutun dönem olduğunu
bilen bir insan olmadan hiçbir anlam taşımaz; TARIFE matrisinin satırlarının dönem, sütunlarının
basamak olduğu hiçbir yerde yazılı değildir; 581 boş hücrenin hangileri olduğu diziden okunamaz.
Dizinin etiketi yoktur. Sonraki konu, veri çerçeveleri, tam bu eksiği ele alır: etiket eklendiğinde
hangi işler kendiliğinden doğru olur ve etiketin üstlendiği hizalama hangi kararı görünmez kılar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.