বিশেষ ম্যাট্রিক্স — ট্রাইডায়াগোনাল ও স্পার্স সিস্টেম
এই পাঠে যা শিখবেন
- ট্রাইডায়াগোনাল ও স্পার্স ম্যাট্রিক্স কী, এবং কোথায় বাস্তবে এগুলো দেখা যায়
- Thomas অ্যালগরিদম (ট্রাইডায়াগোনাল সিস্টেমের জন্য বিশেষায়িত গসিয়ান এলিমিনেশন) হাতে বাস্তবায়ন করা
- কীভাবে একটি অ্যালগরিদমের প্রকৃত arithmetic-অপারেশন সংখ্যা গণনা করা যায় (শুধু Big-O দিয়ে অনুমান নয়)
- একটি সত্যিকারের, চলমান Python ডেমো — বিভিন্ন
n-এ Thomas বনাম সাধারণ গসিয়ান এলিমিনেশনের প্রকৃত অপারেশন-সংখ্যা তুলনা
১ · ট্রাইডায়াগোনাল ও স্পার্স ম্যাট্রিক্স কী
বহু বাস্তব সিস্টেমে ম্যাট্রিক্স A-এর বেশিরভাগ উপাদান শূন্য — একে স্পার্সএমন ম্যাট্রিক্স যার বেশিরভাগ উপাদান শূন্য — সাধারণত অ-শূন্য উপাদানের সংখ্যা ম্যাট্রিক্সের মোট আকারের তুলনায় অনেক কম।
ম্যাট্রিক্স বলা হয়। একটি বিশেষ ও খুবই সাধারণ স্পার্স গঠন হলো ট্রাইডায়াগোনাল — শুধু মূল
ডায়াগোনাল ও তার ঠিক পাশের দুটি ডায়াগোনালে অ-শূন্য মান থাকে। এই গঠন প্রাকৃতিকভাবে দেখা যায় L34, L36-L37-এর
ফাইনাইট-ডিফারেন্স গ্রিডে (প্রতিটি গ্রিড-বিন্দু শুধু তার তাৎক্ষণিক প্রতিবেশীদের সাথে সম্পর্কিত), এবং
L19-এর কিউবিক স্প্লাইনের সমীকরণেও।
২ · Thomas অ্যালগরিদম — বিশেষায়িত গসিয়ান এলিমিনেশন
Thomas অ্যালগরিদম আসলে L10-এর গসিয়ান এলিমিনেশনেরই একটি বিশেষ সংস্করণ — কিন্তু যেহেতু প্রতিটি সারিতে মাত্র তিনটি অ-শূন্য উপাদান আছে, প্রতিটি ধাপে শুধু একটি উপাদান শূন্য করলেই চলে (সাধারণ এলিমিনেশনে পুরো সারি জুড়ে কাজ করতে হয়)। ধাপগুলো:
$$m_i = d_i - a_i \cdot c'_{i-1}, \qquad c'_i = \frac{c_i}{m_i}, \qquad d'_i = \frac{b_i - a_i \cdot d'_{i-1}}{m_i}$$
এখানে a, d, c যথাক্রমে নিচের, মূল, ও উপরের ডায়াগোনাল, এবং
b ডান-পক্ষের ভেক্টর। ফরোয়ার্ড sweep-এর পর একটি সাধারণ ব্যাক-সাবস্টিটিউশনে চূড়ান্ত সমাধান
পাওয়া যায় — মোট খরচ O(n), সাধারণ গসিয়ান এলিমিনেশনের O(n³)-এর তুলনায়
বিশাল সাশ্রয়।
ট্রাইডায়াগোনাল সিস্টেম সংরক্ষণে সম্পূর্ণ
n×n ম্যাট্রিক্সের প্রয়োজন নেই — মাত্র তিনটি length-n অ্যারে (a, d, c) যথেষ্ট, স্মৃতিতেও বিশাল সাশ্রয়।Thomas অ্যালগরিদম রৈখিকভাবে (
n-এর সরাসরি সমানুপাতিক) বাড়ে, যেখানে সাধারণ গসিয়ান এলিমিনেশন ঘনকীয়ভাবে (n-এর ঘনফলের সমানুপাতিক) বাড়ে — বড় n-এ পার্থক্য বিশাল।আরও সাধারণ স্পার্স ম্যাট্রিক্সের জন্য (ট্রাইডায়াগোনাল না হলেও) coordinate-list বা dictionary-of-keys স্টোরেজ ব্যবহার করা হয় — L48-এ বিস্তারিত।
৩ · একটি সত্যিকারের ডেমো — অপারেশন-সংখ্যা তুলনা
নিচে Thomas অ্যালগরিদম ও সাধারণ গসিয়ান এলিমিনেশন উভয়ই একটি OpCounter দিয়ে instrument করা
হয়েছে — প্রতিটি গুণ, ভাগ, যোগ ও বিয়োগ গণনা করা হয়েছে। একই ডায়াগোনালি-ডমিন্যান্ট ট্রাইডায়াগোনাল সিস্টেম
বিভিন্ন আকারে (n = ৫ থেকে ১০০) উভয় পদ্ধতিতে সমাধান করে প্রকৃত অপারেশন-সংখ্যা
তুলনা করা হয়েছে, এবং সমাধান দুটো মিলে যায় কিনা তাও যাচাই করা হয়েছে।
class OpCounter:
def __init__(self):
self.n = 0
def mul(self, a, b):
self.n += 1
return a * b
def div(self, a, b):
self.n += 1
return a / b
def addsub(self, a, b, sign=1):
self.n += 1
return a + sign * b
def make_tridiagonal_system(n):
# ডায়াগোনালি-ডমিন্যান্ট ট্রাইডায়াগোনাল সিস্টেম -- a=নিচের, d=প্রধান, c=উপরের ডায়াগোনাল
a = [0.0] + [1.0] * (n - 1)
d = [4.0] * n
c = [1.0] * (n - 1) + [0.0]
b = [float(i + 1) for i in range(n)]
return a, d, c, b
def thomas_solve(a, d, c, b, counter):
n = len(d)
cp = [0.0] * n
dp = [0.0] * n
cp[0] = counter.div(c[0], d[0])
dp[0] = counter.div(b[0], d[0])
for i in range(1, n):
m = counter.addsub(d[i], counter.mul(a[i], cp[i - 1]), sign=-1)
if i < n - 1:
cp[i] = counter.div(c[i], m)
dp[i] = counter.div(counter.addsub(b[i], counter.mul(a[i], dp[i - 1]), sign=-1), m)
x = [0.0] * n
x[n - 1] = dp[n - 1]
for i in range(n - 2, -1, -1):
x[i] = counter.addsub(dp[i], counter.mul(cp[i], x[i + 1]), sign=-1)
return x
def tri_to_dense(a, d, c, n):
A = [[0.0] * n for _ in range(n)]
for i in range(n):
A[i][i] = d[i]
if i > 0:
A[i][i - 1] = a[i]
if i < n - 1:
A[i][i + 1] = c[i]
return A
def gaussian_solve_counted(A, b, counter):
n = len(A)
M = [row[:] + [b[i]] for i, row in enumerate(A)]
for k in range(n - 1):
for i in range(k + 1, n):
factor = counter.div(M[i][k], M[k][k])
for j in range(k, n + 1):
M[i][j] = counter.addsub(M[i][j], counter.mul(factor, M[k][j]), sign=-1)
x = [0.0] * n
for i in range(n - 1, -1, -1):
s = M[i][n]
for j in range(i + 1, n):
s = counter.addsub(s, counter.mul(M[i][j], x[j]), sign=-1)
x[i] = counter.div(s, M[i][i])
return x
print(f"{'n':>4} | {'Thomas অপারেশন':>16} | {'সাধারণ Gaussian অপারেশন':>24} | {'অনুপাত':>10}")
for n in [5, 10, 20, 50, 100]:
a, d, c, b = make_tridiagonal_system(n)
A = tri_to_dense(a, d, c, n)
ct1 = OpCounter()
x_thomas = thomas_solve(a, d, c, b, ct1)
ct2 = OpCounter()
x_gauss = gaussian_solve_counted(A, b, ct2)
max_diff = max(abs(x_thomas[i] - x_gauss[i]) for i in range(n))
ratio = ct2.n / ct1.n
print(f"{n:>4} | {ct1.n:>16} | {ct2.n:>24} | {ratio:>9.2f}x (সমাধান পার্থক্য সর্বোচ্চ {max_diff:.2e})")
# n=5 কেসে মূল সমীকরণে বসিয়ে যাচাই
n = 5
a, d, c, b = make_tridiagonal_system(n)
A = tri_to_dense(a, d, c, n)
ct = OpCounter()
x = thomas_solve(a, d, c, b, ct)
print()
print(f"n={n} সমাধান (Thomas):", [round(v, 6) for v in x])
print("যাচাই -- মূল Ax=b-তে বসিয়ে অবশিষ্টাংশ:")
for i in range(n):
lhs = sum(A[i][j] * x[j] for j in range(n))
print(f"সমীকরণ {i+1}: LHS = {lhs:.10f} b = {b[i]:.6f} residual = {lhs - b[i]:.2e}")
n | Thomas অপারেশন | সাধারণ Gaussian অপারেশন | অনুপাত 5 | 33 | 135 | 4.09x (সমাধান পার্থক্য সর্বোচ্চ 5.55e-17) 10 | 73 | 895 | 12.26x (সমাধান পার্থক্য সর্বোচ্চ 4.44e-16) 20 | 153 | 6290 | 41.11x (সমাধান পার্থক্য সর্বোচ্চ 8.88e-16) 50 | 393 | 89475 | 227.67x (সমাধান পার্থক্য সর্বোচ্চ 1.78e-15) 100 | 793 | 691450 | 871.94x (সমাধান পার্থক্য সর্বোচ্চ 7.11e-15) n=5 সমাধান (Thomas): [0.167949, 0.328205, 0.519231, 0.594872, 1.101282] যাচাই -- মূল Ax=b-তে বসিয়ে অবশিষ্টাংশ: সমীকরণ 1: LHS = 1.0000000000 b = 1.000000 residual = 0.00e+00 সমীকরণ 2: LHS = 2.0000000000 b = 2.000000 residual = 0.00e+00 সমীকরণ 3: LHS = 3.0000000000 b = 3.000000 residual = 0.00e+00 সমীকরণ 4: LHS = 4.0000000000 b = 4.000000 residual = 0.00e+00 সমীকরণ 5: LHS = 5.0000000000 b = 5.000000 residual = -8.88e-16
n=৫-এ Thomas মাত্র ৪ গুণ কম অপারেশন ব্যবহার করে,
কিন্তু n=১০০-এ এই অনুপাত বেড়ে ৮৭২ গুণ-এ পৌঁছেছে — এটাই O(n) বনাম
O(n³)-এর বাস্তব প্রভাব, শুধু তাত্ত্বিক দাবি নয়। এবং প্রতিটি n-এ উভয় পদ্ধতির
সমাধান মেশিন-প্রিসিশন সীমার মধ্যে (১০⁻¹⁵ থেকে ১০⁻¹⁷ মাত্রায়) মিলে যায় —
Thomas অ্যালগরিদম গতি বাড়ালেও সঠিকতায় কোনো ছাড় দেয় না।
M3 জুড়ে আমরা দেখেছি: গসিয়ান এলিমিনেশন (L10) সবচেয়ে সাধারণ ভিত্তি, LU ডিকম্পোজিশন (L11) সেটিকে পুনঃব্যবহারযোগ্য করে, পিভোটিং (L12) সেটিকে numerically স্থিতিশীল করে, জ্যাকোবি/গস-সেইডেল (L13) বড় স্পার্স সিস্টেমে দ্রুততর বিকল্প দেয়, কন্ডিশন নাম্বার (L14) বলে দেয় কতটা সতর্ক থাকা প্রয়োজন, আর Thomas অ্যালগরিদম (L15) দেখায় — সিস্টেমের বিশেষ গঠন চিনে নিলে সাধারণ অ্যালগরিদমের চেয়ে বহুগুণ দ্রুত সমাধান সম্ভব। M4-এ আমরা এই ভিত্তির উপর দাঁড়িয়ে ইন্টারপোলেশন ও কার্ভ ফিটিং-এ প্রবেশ করব।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
কোড সেলে gaussian_solve_counted ফাংশনটি সম্পূর্ণ n×n ডেন্স ম্যাট্রিক্স
নিয়ে কাজ করে, যদিও বেশিরভাগ উপাদান শূন্য। এতে কী "অপচয়" ঘটছে?
সাধারণ গসিয়ান এলিমিনেশন জানে না ম্যাট্রিক্সটি ট্রাইডায়াগোনাল — তাই এটি প্রতিটি সারির প্রতিটি কলামে এলিমিনেশন চালায়, এমনকি সেগুলো ইতিমধ্যে শূন্য হলেও। এই "শূন্যের সাথে শূন্য কাজ" করাই মূল অপচয় — Thomas অ্যালগরিদম আগে থেকেই জানে কোন উপাদানগুলো শূন্য, তাই সেগুলো স্পর্শই করে না।
প্র ০২
অনুপাত (Thomas বনাম Gaussian) n বাড়ার সাথে সাথে দ্রুত বাড়ছে কেন — এটি কি রৈখিক
হারে বাড়ছে, নাকি দ্রুততর?
অনুপাত O(n³) / O(n) = O(n²) হারে বাড়ে — অর্থাৎ n দ্বিগুণ করলে অনুপাত
প্রায় চারগুণ হওয়া উচিত। ডেটাতেও এটি দেখা যায়: n ১০ থেকে ২০-এ দ্বিগুণ হলে অনুপাত
১২.২৬x থেকে ৪১.১১x-এ বেড়েছে (প্রায় ৩.৪ গুণ, তত্ত্বের কাছাকাছি — ছোট
n-এ constant-factor প্রভাব এখনও কিছুটা রয়ে যায়)।
প্র ০৩ যদি একটি ম্যাট্রিক্স "pentadiagonal" হয় (মূল ডায়াগোনালের দুই পাশে দুটো করে ডায়াগোনাল, মোট পাঁচটি), তাহলে Thomas অ্যালগরিদম সরাসরি প্রয়োগ করা যাবে কি?
না, সরাসরি নয় — Thomas অ্যালগরিদম নির্দিষ্টভাবে তিনটি ডায়াগোনালের গঠনের জন্য ডিজাইন করা। তবে একই মূলনীতি (ম্যাট্রিক্সের বিশেষ ব্যান্ড-গঠন কাজে লাগিয়ে সাধারণ এলিমিনেশনের চেয়ে দ্রুত সমাধান করা) "ব্যান্ড ম্যাট্রিক্স সলভার" নামে সাধারণীকৃত করা যায়, যা pentadiagonal বা যেকোনো নির্দিষ্ট ব্যান্ডউইথের সিস্টেমের জন্য কাজ করে — L48-এ স্পার্স ম্যাট্রিক্স টেকনিকে এই সাধারণীকরণ বিস্তারিত।
অনুশীলন
-
চিন্তা করুন: উপরের কোড সেলে
n = ২০০যোগ করলে অনুপাত (ratio) মোটামুটি কত হবে বলে আপনার ধারণা, যেহেতুn=১০০-এ অনুপাত৮৭১.৯৪x?যেহেতু অনুপাত
O(n²)হারে বাড়ে,nদ্বিগুণ (১০০ → ২০০) করলে অনুপাত প্রায় চারগুণ হওয়া উচিত — অর্থাৎ মোটামুটি৩,৪০০x-এর কাছাকাছি (আরও বড়n-এ constant-factor প্রভাব কমে যাওয়ায় তাত্ত্বিক অনুপাতের আরও কাছাকাছি পৌঁছানো উচিত)। -
পরীক্ষা করুন: উপরের কোড সেলে
for n in [5, 10, 20, 50, 100]:লাইনে200যোগ করে Run চেপে প্রকৃত অনুপাত যাচাই করুন।n=200-এ প্রকৃত অনুপাত~৩,৪০০x-এর কাছাকাছি আসে (তাত্ত্বিকO(n²)স্কেলিং-এর সাথে সামঞ্জস্যপূর্ণ) — নিশ্চিত করে যে বড় সিস্টেমে বিশেষায়িত অ্যালগরিদম ব্যবহারের গুরুত্ব আরও তীব্রভাবে বাড়ে, ছোট সিস্টেমে নয়।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ এরর অ্যানালাইসিস, রুট-ফাইন্ডিং, লিনিয়ার সিস্টেম, ইন্টারপোলেশন, নিউমেরিক্যাল ইন্টিগ্রেশন, ODE সলভিং, আইগেনভ্যালু মেথড, অপ্টিমাইজেশন ও ক্যাপস্টোন।
- Design and Analysis of Algorithms সহোদর কোর্স Big-O কমপ্লেক্সিটির আনুষ্ঠানিক প্রমাণ ও বিশ্লেষণ পদ্ধতি — এই কোর্স সেই একই কমপ্লেক্সিটি ধারণা সংখ্যাগত অ্যালগরিদমে প্রয়োগ করে।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, Machine Learning, Deep Learning, System Design, Cybersecurity, Cloud Computing & DevOps, এবং আরও অনেক কোর্স।