পাঠ ১১ · ৫৭-এর মধ্যে · মডিউল ৩
Home / Courses / Numerical Methods / LU ডিকম্পোজিশন

LU ডিকম্পোজিশন

LU decomposition
১০ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • LU ডিকম্পোজিশনের সংজ্ঞা এবং কেন এটি গসিয়ান এলিমিনেশনেরই একটি পুনর্গঠিত রূপ
  • Doolittle's method দিয়ে হাতে L ও U বের করার পদ্ধতি
  • Forward substitution (Ly = b) ও back substitution (Ux = y) দিয়ে সিস্টেম সমাধান করা
  • একটি সত্যিকারের, চলমান Python ডেমো — L*U পুনর্গঠন যাচাই এবং একই ফ্যাক্টর দিয়ে সিস্টেম সমাধান

১ · কেন A-কে ভাঙা হয় L ও U-তে

L10-এ আমরা দেখেছি গসিয়ান এলিমিনেশন প্রতিটি সারি থেকে একটি factor গুণ বিয়োগ করে A-কে upper-triangular U-তে রূপান্তর করে। মজার বিষয় হলো — সেই factor-গুলো ফেলে না দিয়ে যদি একটি আলাদা lower-triangular ম্যাট্রিক্স L-এ সাজিয়ে রাখি (ডায়াগোনালে ১ বসিয়ে), তাহলে:

$$A = LU$$

এই ফ্যাক্টরাইজেশনের সুবিধা তখন বোঝা যায় যখন একই A-এর জন্য একাধিক ভিন্ন b নিয়ে সমাধান করতে হয় (যেমন সিমুলেশনে বারবার ভিন্ন ইনপুট)। L ও U একবার গণনা করে রাখলে, প্রতিটি নতুন b-এর জন্য শুধু দুটি সস্তা O(n²) ধাপ (forward + back substitution) লাগে — সম্পূর্ণ O(n³) এলিমিনেশন পুনরায় না করেই।

২ · Doolittle's method — হাতে L, U বের করা

Doolittle's method-এ U-এর প্রতিটি সারি এবং L-এর প্রতিটি কলাম একই সাথে ধাপে ধাপে গণনা করা হয়:

$$U_{kj} = A_{kj} - \sum_{m=0}^{k-1} L_{km}U_{mj}, \qquad L_{ik} = \frac{A_{ik} - \sum_{m=0}^{k-1} L_{im}U_{mk}}{U_{kk}}$$

এখানে k প্রতিটি ধাপে বাড়ে, এবং L-এর ডায়াগোনাল সবসময় ১ ধরা হয় (এ কারণে একে "Doolittle" ফর্ম বলা হয় — বিকল্প হিসেবে "Crout's method"-এ U-এর ডায়াগোনাল ১ রাখা হয়)।

Forward substitution
Ly = b সমাধান করে y বের করা — যেহেতু L lower-triangular, প্রথম সারি থেকে শুরু করে সরাসরি সমাধান হয়।
Back substitution
Ux = y সমাধান করে চূড়ান্ত x বের করা — L10-এর মতোই, শেষ সারি থেকে উপরে।
পুনঃব্যবহারযোগ্যতা
একই L, U একাধিকবার ব্যবহার করা যায় — L39-এ ইনভার্স পাওয়ার মেথডে এই কৌশল পুনরায় কাজে লাগবে।

৩ · একটি সত্যিকারের ডেমো — LU ফ্যাক্টরাইজেশন ও সিস্টেম সমাধান

নিচে একটি 3x3 ম্যাট্রিক্স A-কে L ও U-তে ভাঙা হয়েছে, তারপর L*U গণনা করে সরাসরি মূল A-এর সাথে তুলনা করে দেখানো হয়েছে এটি হুবহু পুনর্গঠিত হয়। এরপর Ly = b ও Ux = y সমাধান করে চূড়ান্ত x বের করা হয়েছে এবং মূল সমীকরণে বসিয়ে যাচাই করা হয়েছে।

Python
def lu_decompose(A):
    n = len(A)
    L = [[0.0] * n for _ in range(n)]
    U = [[0.0] * n for _ in range(n)]
    for i in range(n):
        L[i][i] = 1.0

    for k in range(n):
        for j in range(k, n):
            U[k][j] = A[k][j] - sum(L[k][m] * U[m][j] for m in range(k))
        for i in range(k + 1, n):
            L[i][k] = (A[i][k] - sum(L[i][m] * U[m][k] for m in range(k))) / U[k][k]
    return L, U

def mat_mult(X, Y):
    n = len(X)
    p = len(Y[0])
    m = len(Y)
    return [[sum(X[i][t] * Y[t][j] for t in range(m)) for j in range(p)] for i in range(n)]

def forward_sub(L, b):
    n = len(L)
    y = [0.0] * n
    for i in range(n):
        y[i] = b[i] - sum(L[i][j] * y[j] for j in range(i))
    return y

def back_sub(U, y):
    n = len(U)
    x = [0.0] * n
    for i in range(n - 1, -1, -1):
        x[i] = (y[i] - sum(U[i][j] * x[j] for j in range(i + 1, n))) / U[i][i]
    return x

A = [
    [4.0,  3.0,  2.0],
    [2.0, -1.0,  1.0],
    [6.0,  3.0,  5.0],
]
b = [21.0, 3.0, 26.0]

L, U = lu_decompose(A)
print("L =")
for row in L:
    print(["%.6f" % v for v in row])
print("U =")
for row in U:
    print(["%.6f" % v for v in row])

LU = mat_mult(L, U)
print()
print("যাচাই -- L*U পুনর্গঠন বনাম মূল A:")
max_diff = 0.0
for i in range(3):
    for j in range(3):
        diff = abs(LU[i][j] - A[i][j])
        max_diff = max(max_diff, diff)
print(f"সর্বোচ্চ পার্থক্য = {max_diff:.2e}")

y = forward_sub(L, b)
x = back_sub(U, y)
print()
print("Ly = b থেকে y =", [round(v, 6) for v in y])
print("Ux = y থেকে x =", [round(v, 6) for v in x])

print()
print("যাচাই -- মূল Ax = b-তে বসিয়ে অবশিষ্টাংশ:")
for i in range(3):
    lhs = sum(A[i][j] * x[j] for j in range(3))
    print(f"সমীকরণ {i+1}: LHS = {lhs:.10f}   b = {b[i]:.6f}   residual = {lhs - b[i]:.2e}")
L =
['1.000000', '0.000000', '0.000000']
['0.500000', '1.000000', '0.000000']
['1.500000', '0.600000', '1.000000']
U =
['4.000000', '3.000000', '2.000000']
['0.000000', '-2.500000', '0.000000']
['0.000000', '0.000000', '2.000000']

যাচাই -- L*U পুনর্গঠন বনাম মূল A:
সর্বোচ্চ পার্থক্য = 0.00e+00

Ly = b থেকে y = [21.0, -7.5, -1.0]
Ux = y থেকে x = [3.25, 3.0, -0.5]

যাচাই -- মূল Ax = b-তে বসিয়ে অবশিষ্টাংশ:
সমীকরণ 1: LHS = 21.0000000000   b = 21.000000   residual = 0.00e+00
সমীকরণ 2: LHS = 3.0000000000   b = 3.000000   residual = 0.00e+00
সমীকরণ 3: LHS = 26.0000000000   b = 26.000000   residual = 0.00e+00
L*U মূল A-এর সাথে সর্বোচ্চ 0.00e+00 পার্থক্যে মিলে যায় — অর্থাৎ ফ্যাক্টরাইজেশনটি হুবহু সঠিক। এরপর একই L, U ব্যবহার করে x = [3.25, 3.0, -0.5] পাওয়া গেছে, এবং মূল সমীকরণে বসিয়ে সবগুলো residual শূন্য — সমাধানটি সত্যিই সঠিক।
মূল কথা · Key takeaway

LU ডিকম্পোজিশন গসিয়ান এলিমিনেশনের একই কাজ করে, কিন্তু মধ্যবর্তী তথ্য (factor-গুলো) সংরক্ষণ করে পুনঃব্যবহারযোগ্য করে তোলে। L12-এ আমরা দেখব কখন এই সরল Doolittle পদ্ধতি ব্যর্থ হয় (শূন্য পিভোট) এবং কীভাবে পিভোটিং দিয়ে PA = LU আকারে এটি সংশোধন করা হয়।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ L-এর ডায়াগোনালে সবসময় ১ কেন রাখা হয় — এটি কি স্বেচ্ছাচারী পছন্দ, নাকি এর পেছনে কারণ আছে?

এটি একটি convention — A = LU সমীকরণে n×n মাত্রার A-এর জন্য মোট n²টি সমীকরণ পাওয়া যায়, কিন্তু L ও U মিলিয়ে অজানা রাশি আছে n² + nটি (উভয় ম্যাট্রিক্সের ডায়াগোনাল গুনলে)। তাই সিস্টেমটি সমাধানযোগ্য করতে nটি অতিরিক্ত শর্ত দরকার — Doolittle পদ্ধতিতে L-এর ডায়াগোনাল ১ ধরে এই স্বাধীনতা ব্যবহার করা হয় (Crout's method বিকল্পভাবে U-এর ডায়াগোনাল ১ ধরে)।

প্র ০২ যদি একই A-এর জন্য দশটি ভিন্ন b ভেক্টর সমাধান করতে হয়, তাহলে LU ডিকম্পোজিশন গসিয়ান এলিমিনেশনের চেয়ে কতটা সুবিধাজনক?

গসিয়ান এলিমিনেশন সরাসরি ব্যবহার করলে প্রতিটি b-এর জন্য পুরো O(n³) এলিমিনেশন পুনরায় করতে হতো — দশবার। LU ডিকম্পোজিশনে L, U একবার (O(n³)) গণনা করে রাখলে, প্রতিটি নতুন b-এর জন্য শুধু O(n²) খরচের forward+back substitution লাগে — বড় n-এর জন্য এটি উল্লেখযোগ্য সাশ্রয়।

প্র ০৩ কোড সেলে U[k][k] শূন্য হলে L[i][k] গণনায় কী সমস্যা হবে?

L[i][k] = (...) / U[k][k] লাইনে ZeroDivisionError ঘটবে — ঠিক যেমন L10-এর গসিয়ান এলিমিনেশনে শূন্য পিভোটের সমস্যা হয়েছিল, কারণ LU ডিকম্পোজিশন মূলত সেই একই এলিমিনেশন প্রক্রিয়া। এই সমস্যার সমাধান একই — পিভোটিং, যা L12-এ বিস্তারিত আলোচনা করা হবে।

অনুশীলন

  1. চিন্তা করুন: উপরের কোড সেলে U-এর ডায়াগোনাল উপাদানগুলো (4, -2.5, 2) গুণ করলে কী পাওয়া যায় বলে আপনার মনে হয়, এবং এটি A-এর কোন বৈশিষ্ট্যের সাথে সম্পর্কিত হতে পারে?

    4 × (-2.5) × 2 = -20। এটি আসলে A-এর ডিটারমিন্যান্ট — যেহেতু det(L) = 1 (ত্রিভুজাকার ম্যাট্রিক্সের ডিটারমিন্যান্ট = ডায়াগোনালের গুণফল, আর L-এর ডায়াগোনাল সব ১), তাই det(A) = det(L) × det(U) = det(U) — অর্থাৎ U-এর ডায়াগোনালের গুণফলই A-এর ডিটারমিন্যান্ট।

  2. পরীক্ষা করুন: কোড সেলে print(U[0][0] * U[1][1] * U[2][2]) যোগ করে Run চেপে আপনার অনুমান যাচাই করুন।

    এই কোড -20.0 প্রিন্ট করবে — ঠিক যেমন অনুমান করা হয়েছিল। এই সম্পর্কটি ব্যবহারিকভাবে গুরুত্বপূর্ণ: 3×3 বা তার বড় ম্যাট্রিক্সের ডিটারমিন্যান্ট সরাসরি cofactor expansion দিয়ে গণনা করা ব্যয়বহুল (O(n!)), কিন্তু LU ডিকম্পোজিশনের মাধ্যমে মাত্র O(n³)-এ পাওয়া যায়।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
গসিয়ান এলিমিনেশন