পাঠ ১২ · ৫৭-এর মধ্যে · মডিউল ৩
Home / Courses / Numerical Methods / পিভোটিং স্ট্র্যাটেজি

পিভোটিং স্ট্র্যাটেজি ও নিউমেরিক্যাল স্ট্যাবিলিটি

Pivoting strategies & numerical stability
১০ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

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

  • কেন ছোট পিভোট numerical instability তৈরি করে
  • Partial pivoting কীভাবে কাজ করে — প্রতিটি ধাপে সারি অদলবদল করে বড় পিভোট নিশ্চিত করা
  • একটি সত্যিকারের, চলমান Python ডেমো — সীমিত-নির্ভুলতার arithmetic-এ naive বনাম pivoted এলিমিনেশনের প্রকৃত পার্থক্য
  • near-singular সিস্টেম চেনার লক্ষণ ও পিভোটিং কীভাবে ill-conditioning সম্পূর্ণ সমাধান করে না তা বোঝা

১ · ছোট পিভোট কেন বিপজ্জনক

L10-এ আমরা দেখেছি factor = M[i][k] / M[k][k]। যদি পিভোট M[k][k] খুব ছোট হয় (যেমন ০.০০০১) আর বাকি সারির মান তুলনামূলক বড়, তাহলে factor খুব বড় সংখ্যা হয়ে যায়। এরপর M[i][j] -= factor * M[k][j] ধাপে একটি বড় সংখ্যা থেকে আরেকটি বড় সংখ্যা বিয়োগ হয় — এবং সীমিত-নির্ভুলতার arithmetic-এ (বাস্তব কম্পিউটার সবসময় সীমিত সংখ্যক সিগনিফিক্যান্ট ডিজিট সংরক্ষণ করে, L02-এ বিস্তারিত) এই ধরনের বিয়োগে গুরুত্বপূর্ণ তথ্য হারিয়ে যেতে পারে।

২ · Partial Pivoting — সমাধান

Partial pivoting-এর নিয়ম সহজ: প্রতিটি এলিমিনেশন ধাপে, বর্তমান কলামে (বর্তমান সারি থেকে নিচের সব সারির মধ্যে) সবচেয়ে বড় absolute মান খুঁজে সেই সারিকে বর্তমান সারির সাথে অদলবদল (swap) করে নেওয়া হয় — তারপর এলিমিনেশন করা হয়। এতে নিশ্চিত হয় |factor| ≤ ১ সবসময়, যা arithmetic-কে অনেক বেশি স্থিতিশীল করে তোলে। L10-L11-এর কোড সেলে ব্যবহৃত সিস্টেমগুলো এমনিতেই যথেষ্ট ভালো পিভোট নিয়ে সাজানো ছিল বলে pivoting ছাড়াও কাজ করেছে — কিন্তু সব সিস্টেম এত সহযোগী নয়।

Near-singular সিস্টেম
যে সিস্টেমে একটি পিভোট শূন্যের খুব কাছাকাছি — এমনকি সঠিক সমাধান থাকলেও, সরাসরি এলিমিনেশন গুরুতরভাবে ভুল ফলাফল দিতে পারে সীমিত-নির্ভুলতার arithmetic-এ।
Partial vs. Complete pivoting
Partial pivoting শুধু সারি অদলবদল করে (দ্রুত, বাস্তবে প্রায় সবসময় যথেষ্ট); complete pivoting সারি ও কলাম উভয়ই অদলবদল করতে পারে (আরও নিরাপদ কিন্তু ব্যয়বহুল) — production লাইব্রেরি সাধারণত partial pivoting ব্যবহার করে।
পিভোটিং সবকিছুর সমাধান নয়
যদি সিস্টেমটি নিজেই ill-conditioned হয় (L14-এ বিস্তারিত), পিভোটিং সাহায্য করে কিন্তু সমস্যাটি সম্পূর্ণ দূর করে না — সেটি সিস্টেমের অন্তর্নিহিত বৈশিষ্ট্য, এলিমিনেশন-পদ্ধতির ত্রুটি নয়।

৩ · একটি সত্যিকারের ডেমো — Naive বনাম Pivoted এলিমিনেশন

নিচের ডেমোতে একটি ২x২ সিস্টেম ব্যবহার করা হয়েছে যার একটি পিভোট ইচ্ছাকৃতভাবে খুব ছোট (০.০০০১):

$$0.0001\,x_1 + x_2 = 1, \qquad x_1 + x_2 = 2$$

আসল কম্পিউটার হার্ডওয়্যারের সীমিত-নির্ভুলতার আচরণ সিমুলেট করতে, একটি round_sig() ফাংশন লেখা হয়েছে যা প্রতিটি arithmetic অপারেশনের পর ফলাফল মাত্র ৩টি সিগনিফিক্যান্ট ডিজিটে রাউন্ড করে (একটি কাল্পনিক, খুবই সীমিত-নির্ভুলতার মেশিনের মতো — বাস্তব double-precision অনেক বেশি নির্ভুল, কিন্তু এই সিমুলেশন pivoting-এর প্রভাব স্পষ্টভাবে দৃশ্যমান করে তোলে)। একই সিস্টেম প্রথমে pivoting ছাড়া, তারপর partial pivoting সহ সমাধান করে পূর্ণ-নির্ভুলতার রেফারেন্স সমাধানের সাথে তুলনা করা হয়েছে।

Python
import math

def round_sig(x, sig=3):
    # সিমুলেট করে একটি সীমিত-নির্ভুলতার (limited-precision) মেশিন -- প্রতিটি arithmetic
    # অপারেশনের পর ফলাফল নির্দিষ্টসংখ্যক সিগনিফিক্যান্ট ডিজিটে রাউন্ড করা হয়
    if x == 0:
        return 0.0
    d = math.ceil(math.log10(abs(x)))
    power = sig - d
    factor = 10 ** power
    return round(x * factor) / factor

def solve_2x2(A, b, pivot, sig=3):
    M = [A[0][:] + [b[0]], A[1][:] + [b[1]]]

    if pivot and abs(M[1][0]) > abs(M[0][0]):
        M[0], M[1] = M[1], M[0]

    factor = round_sig(M[1][0] / M[0][0], sig)
    for j in range(3):
        M[1][j] = round_sig(M[1][j] - round_sig(factor * M[0][j], sig), sig)

    x2 = round_sig(M[1][2] / M[1][1], sig)
    x1 = round_sig(round_sig(M[0][2] - round_sig(M[0][1] * x2, sig), sig) / M[0][0], sig)
    return [x1, x2]

A = [
    [0.0001, 1.0],
    [1.0,    1.0],
]
b = [1.0, 2.0]

# পূর্ণ-নির্ভুলতায় (double precision) প্রকৃত সমাধান -- Cramer's rule দিয়ে রেফারেন্স
det = A[0][0] * A[1][1] - A[0][1] * A[1][0]
true_x1 = (b[0] * A[1][1] - A[0][1] * b[1]) / det
true_x2 = (A[0][0] * b[1] - b[0] * A[1][0]) / det
print(f"রেফারেন্স সমাধান (পূর্ণ ডাবল-প্রিসিশন): x1 = {true_x1:.8f}   x2 = {true_x2:.8f}")

sig = 3
x_naive = solve_2x2(A, b, pivot=False, sig=sig)
x_pivot = solve_2x2(A, b, pivot=True, sig=sig)

print()
print(f"{sig}-সিগনিফিক্যান্ট-ডিজিট মেশিনে, পিভোটিং ছাড়া (naive):")
print(f"  x1 = {x_naive[0]}   x2 = {x_naive[1]}")
print(f"  x1-এর প্রকৃত ভুল = {abs(x_naive[0] - true_x1):.6f}   x2-এর প্রকৃত ভুল = {abs(x_naive[1] - true_x2):.6f}")

print()
print(f"{sig}-সিগনিফিক্যান্ট-ডিজিট মেশিনে, partial pivoting সহ:")
print(f"  x1 = {x_pivot[0]}   x2 = {x_pivot[1]}")
print(f"  x1-এর প্রকৃত ভুল = {abs(x_pivot[0] - true_x1):.6f}   x2-এর প্রকৃত ভুল = {abs(x_pivot[1] - true_x2):.6f}")
রেফারেন্স সমাধান (পূর্ণ ডাবল-প্রিসিশন): x1 = 1.00010001   x2 = 0.99989999

3-সিগনিফিক্যান্ট-ডিজিট মেশিনে, পিভোটিং ছাড়া (naive):
  x1 = 0.0   x2 = 1.0
  x1-এর প্রকৃত ভুল = 1.000100   x2-এর প্রকৃত ভুল = 0.000100

3-সিগনিফিক্যান্ট-ডিজিট মেশিনে, partial pivoting সহ:
  x1 = 1.0   x2 = 1.0
  x1-এর প্রকৃত ভুল = 0.000100   x2-এর প্রকৃত ভুল = 0.000100
এটাই পিভোটিং-এর প্রভাবের একটি নাটকীয়, সত্যিকারের উদাহরণ: pivoting ছাড়া x1-এর গণনাকৃত মান ০.০ — যা রেফারেন্স মানের (১.০০০১) তুলনায় প্রায় সম্পূর্ণ ভুল (ভুল ≈ ১.০, অর্থাৎ প্রায় ১০০%)। অথচ partial pivoting সহ একই সিস্টেমে x1 = ১.০ পাওয়া গেছে — ভুল মাত্র ০.০০০১, ~১০,০০০ গুণ বেশি নির্ভুল। উভয় ক্ষেত্রেই ঠিক একই সিস্টেম, একই arithmetic-নির্ভুলতা — শুধু পার্থক্য কোন সারিকে পিভোট হিসেবে বেছে নেওয়া হয়েছে তাতে।
মূল কথা · Key takeaway

পিভোটিং কোনো "ভালো অভ্যাস" নয় — এটি numerical linear algebra-র একটি বাধ্যতামূলক নিরাপত্তা ব্যবস্থা। সব production-grade সলভার (এমনকি L11-এর LU ডিকম্পোজিশনও বাস্তবে PA = LU আকারে, যেখানে P একটি পারমুটেশন ম্যাট্রিক্স যা সারি-অদলবদল প্রতিনিধিত্ব করে) ডিফল্টভাবে পিভোটিং ব্যবহার করে। L14-এ আমরা দেখব কীভাবে কন্ডিশন নাম্বার দিয়ে বোঝা যায় একটি সিস্টেম কতটা "ঝুঁকিপূর্ণ" — পিভোটিং যা সমাধান করে না তার পরিমাপ।

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

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

প্র ০১ কোড সেলে round_sig() ফাংশনটি প্রতিটি arithmetic অপারেশনের পর কেন প্রয়োগ করা হয়েছে — শুধু চূড়ান্ত উত্তরে নয়?

বাস্তব কম্পিউটার হার্ডওয়্যারে প্রতিটি স্বতন্ত্র arithmetic অপারেশন (যোগ, বিয়োগ, গুণ, ভাগ) সীমিত সংখ্যক বিটে ফলাফল সংরক্ষণ করে — শুধু চূড়ান্ত ফলাফল নয়, প্রতিটি মধ্যবর্তী ধাপও। যদি শুধু চূড়ান্ত উত্তরে রাউন্ডিং প্রয়োগ করা হতো, তাহলে মধ্যবর্তী ধাপের প্রকৃত precision loss (যা naive এলিমিনেশনে ঘটে) সিমুলেট করা যেত না — এবং pivoting-এর প্রকৃত সুবিধা দেখানো যেত না।

প্র ০২ Pyodide-এর Python নিজে double-precision (৬৪-বিট) floating-point ব্যবহার করে, যা ৩-সিগনিফিক্যান্ট-ডিজিট মেশিনের চেয়ে অনেক বেশি নির্ভুল। তাহলে বাস্তব double-precision arithmetic-এও কি পিভোটিং প্রাসঙ্গিক?

হ্যাঁ — double precision-এ ভুলের মাত্রা অনেক ছোট (৩-ডিজিট সিমুলেশনের মতো নাটকীয় নয়), কিন্তু বড় ম্যাট্রিক্স (শত-হাজার সারি) বা বহু ধাপের এলিমিনেশনে ছোট রাউন্ড-অফ ভুল ক্রমান্বয়ে জমা হতে পারে (L03-এ round-off accumulation বিস্তারিত)। এছাড়া অনেক বাস্তব ইঞ্জিনিয়ারিং সিস্টেম স্বাভাবিকভাবেই ill-conditioned হয় (L14), যেখানে পিভোটিং ছাড়া double precision-এও উল্লেখযোগ্য ভুল দেখা দিতে পারে।

প্র ০৩ যদি প্রথম সারির পিভোট (০.০০০১) দ্বিতীয় সারির চেয়ে বড় হতো, তাহলে partial pivoting কি সারি অদলবদল করত?

না — if abs(M[1][0]) > abs(M[0][0]) শর্তটি শুধু তখনই সারি অদলবদল করে যখন নিচের সারির পিভোট-কলাম মান বর্তমান পিভোটের চেয়ে বড় হয়। যদি বর্তমান পিভোট ইতিমধ্যে সবচেয়ে বড় হয়, তাহলে অদলবদলের কোনো প্রয়োজন নেই — অ্যালগরিদমটি স্বয়ংক্রিয়ভাবে সেই ক্ষেত্রে অপরিবর্তিত থাকে।

অনুশীলন

  1. চিন্তা করুন: উপরের কোড সেলে sig-এর মান ৩ থেকে ৬-এ বাড়ালে (আরও বেশি সিগনিফিক্যান্ট ডিজিট রাখলে) naive সমাধানের ভুল কি কমবে, বাড়বে, নাকি একই থাকবে বলে আপনার মনে হয়?

    বেশি সিগনিফিক্যান্ট ডিজিট রাখলে naive সমাধানের ভুল কমবে, কারণ মেশিনটি তখন প্রকৃত double-precision arithmetic-এর কাছাকাছি চলে যায় — কিন্তু ভুল সম্পূর্ণ দূর হবে না যতক্ষণ না পিভোটিং প্রয়োগ করা হয়, কারণ সমস্যাটি মৌলিকভাবে ছোট পিভোট নিয়ে, শুধু precision-এর অভাব নিয়ে নয়।

  2. পরীক্ষা করুন: উপরের কোড সেলে sig = 3-কে sig = 6-এ পরিবর্তন করে Run চেপে naive ও pivoted উভয় সমাধানের ভুল তুলনা করুন।

    sig = 6-এ naive সমাধানের ভুল অনেক কমে যায় (কারণ এখন মেশিনটি প্রায় প্রকৃত মান ধরে রাখতে পারছে), কিন্তু pivoted সমাধান এখনও একটু বেশি নির্ভুল থাকে। এটি দেখায় — precision বাড়ানো সমস্যাটি "লুকিয়ে" ফেলতে পারে, কিন্তু পিভোটিং সমস্যাটি মূল থেকে এড়িয়ে যায়, যা অনেক বেশি নির্ভরযোগ্য কৌশল।

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

  • কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ এরর অ্যানালাইসিস, রুট-ফাইন্ডিং, লিনিয়ার সিস্টেম, ইন্টারপোলেশন, নিউমেরিক্যাল ইন্টিগ্রেশন, ODE সলভিং, আইগেনভ্যালু মেথড, অপ্টিমাইজেশন ও ক্যাপস্টোন।
  • Design and Analysis of Algorithms সহোদর কোর্স অ্যালগরিদমের সাধারণ জটিলতা-প্রমাণ ও worst-case বিশ্লেষণ — এই কোর্স সেই একই রিগর নিয়ে নিউমেরিক্যাল সঠিকতার প্রশ্নে ফোকাস করে।
  • সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, Machine Learning, Deep Learning, System Design, Cybersecurity, Cloud Computing & DevOps, এবং আরও অনেক কোর্স।
আগের পাঠ
LU ডিকম্পোজিশন