পাঠ ০৬ · ৫৭-এর মধ্যে · মডিউল ২
Home / Courses / Numerical Methods / ফিক্সড-পয়েন্ট ইটারেশন

ফিক্সড-পয়েন্ট ইটারেশন

Fixed-point iteration
৭ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

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

  • একটি সমীকরণকে x = g(x) আকারে কীভাবে পুনর্লিখন করতে হয়
  • ফিক্সড-পয়েন্ট ইটারেশন অ্যালগরিদম বাস্তবায়ন করা এবং একটি কনভার্জিং উদাহরণে এর আচরণ পর্যবেক্ষণ করা
  • একটি খারাপভাবে বেছে নেওয়া g(x)-এর জন্য সত্যিকারের ডাইভার্জেন্স দেখা এবং কেন তা ঘটে বোঝা
  • কনভারজেন্স শর্ত |g'(x)| < 1-এর পেছনের গাণিতিক যুক্তি বোঝা
  • কীভাবে সংখ্যাগতভাবে অতি বড় মান নিরাপদে গার্ড করে কোড ক্র্যাশ ছাড়াই ডাইভার্জেন্স প্রদর্শন করা যায়

১ · x = g(x) আকারে পুনর্লিখন

ফিক্সড-পয়েন্ট ইটারেশনFixed-point iterationএকটি সমীকরণ f(x) = 0-কে x = g(x) আকারে পুনর্লিখন করে x_{n+1} = g(x_n) সূত্র বারবার প্রয়োগ করে রুট খোঁজার মেথড — যেই বিন্দুতে x = g(x), তাকে g-এর "ফিক্সড পয়েন্ট" বলা হয়। বাইসেকশন (L05) শুধু ফাংশনের চিহ্ন ব্যবহার করত এবং প্রতি ধাপে একটি ব্র্যাকেট প্রয়োজন ছিল। ফিক্সড-পয়েন্ট ইটারেশন সম্পূর্ণ ভিন্ন পথ নেয়: সমীকরণ f(x) = 0-কে বীজগাণিতিকভাবে x = g(x) আকারে সাজিয়ে, একটি প্রাথমিক অনুমান x₀ থেকে শুরু করে বারবার x_{n+1} = g(x_n) গণনা করা হয়। যেই বিন্দুতে সিকোয়েন্সটি স্থির হয়ে যায় (x = g(x)), সেটাই ফাংশনের ফিক্সড পয়েন্ট — এবং একটু বীজগণিত করলে দেখা যায় সেটাই মূল সমীকরণের রুট।

একই সমীকরণকে একাধিকভাবে x = g(x) আকারে লেখা যায়। L05-এর সমীকরণ f(x) = x³ - x - 2 = 0 নিয়ে দুটি ভিন্ন পুনর্লিখন বিবেচনা করা যাক:

$$g_1(x) = \sqrt[3]{x + 2} \qquad \text{এবং} \qquad g_2(x) = x^3 - 2$$

দুটোই বীজগাণিতিকভাবে সঠিক — উভয়ের ফিক্সড পয়েন্টেই মূল সমীকরণের রুট। কিন্তু নিচে দেখা যাবে একটি চমৎকারভাবে কনভার্জ করে, আর অন্যটি ভয়াবহভাবে ডাইভার্জ করে — শুধু বীজগণিতে সঠিক হওয়াই যথেষ্ট নয়।

২ · কনভার্জিং উদাহরণ — g₁(x) = (x+2)^(1/3)

নিচের কোড সেলে x₀ = 1.0 থেকে শুরু করে g₁ দিয়ে ইটারেট করা হয়েছে, এবং প্রতিবার L05-এর রেফারেন্স মূল 1.5213797068-এর বিপরীতে প্রকৃত এরর দেখানো হয়েছে।

Python
reference_root = 1.5213797068045678  # L05-এ বের করা x**3 - x - 2 = 0-এর মূল

def g_conv(x):
    return (x + 2) ** (1 / 3)

print("=== কনভার্জিং: g(x) = (x+2)^(1/3) ===")
x = 1.0
print(f"{'ইটার':>4} {'x':>14} {'এরর':>14}")
for i in range(1, 11):
    x_new = g_conv(x)
    err = abs(x_new - reference_root)
    print(f"{i:4d} {x_new:14.8f} {err:14.10f}")
    x = x_new

# ফিক্সড পয়েন্টের কাছে ঢাল (derivative) পরীক্ষা
def gprime_conv(x):
    return (1 / 3) * (x + 2) ** (-2 / 3)

print()
print(f"g'(root) = {gprime_conv(reference_root):.6f}   (|g'(root)| < 1 → কনভার্জ করবে)")

    
কোডটি চালালে দেখা যায় মাত্র ১০ ইটারেশনে এরর ১.৪৯ × ১০⁻¹⁰-এ নেমে আসে (প্রথম ইটারেশনে এরর ছিল ০.০৭৯১) — এবং g'(root) ≈ 0.1440, যা 1-এর চেয়ে অনেক ছোট। প্রতি ইটারেশনে এরর মোটামুটি 0.144 গুণ হয়ে যাচ্ছে — এটাই লিনিয়ার কনভারজেন্স, কিন্তু বাইসেকশনের 0.5 অনুপাতের চেয়ে অনেক দ্রুত।

৩ · ডাইভার্জিং উদাহরণ — g₂(x) = x³ - 2

এবার একই মূল সমীকরণের একটি ভিন্ন, সমান বৈধ পুনর্লিখন ব্যবহার করা যাক। এখানে শুরুর বিন্দু x₀ = 1.4 — রুটের (1.5214) খুবই কাছাকাছি, তারপরও কী হয় দেখা যাক। যেহেতু এটি খুব দ্রুত অত্যন্ত বড় মানে পৌঁছাতে পারে, কোডে একটি সেফটি-গার্ড (magnitude 10¹²-এর বেশি হলে থামা) রাখা হয়েছে যাতে সেল ক্র্যাশ বা হ্যাং না করে।

Python
def g_div(x):
    return x ** 3 - 2

print("=== ডাইভার্জিং: g(x) = x^3 - 2 ===")
x = 1.4  # রুটের খুব কাছাকাছি শুরু, তবুও...
print(f"{'ইটার':>4} {'x':>20}")
for i in range(1, 11):
    try:
        x_new = g_div(x)
    except OverflowError:
        print(f"{i:4d}  OverflowError -- x অসীমের দিকে ডাইভার্জ করেছে")
        break
    print(f"{i:4d} {x_new:20.4f}")
    x = x_new
    if abs(x) > 1e12:
        print("      ... মান 1e12 ছাড়িয়ে গেছে, স্পষ্টভাবে ডাইভার্জ করছে, থামানো হলো")
        break

def gprime_div(x):
    return 3 * x ** 2

print()
print(f"g'(root) = {gprime_div(1.5213797068045678):.4f}   (|g'(root)| > 1 → ডাইভার্জ করবে)")

    
ফলাফলটি চমকপ্রদ — x₀ = 1.4 রুটের এত কাছে হওয়া সত্ত্বেও, মাত্র ৬টি ইটারেশনের মধ্যে মান -১.১৪ × ১০²¹-এ পৌঁছে যায়! আর g'(root) = 6.9438 — যা 1-এর চেয়ে অনেক বড়। এটাই দেখায় ফিক্সড-পয়েন্ট ইটারেশনের সবচেয়ে বড় দুর্বলতা: শুরুর বিন্দু রুটের কতটা কাছে তা গুরুত্বপূর্ণ নয় যদি |g'(x)| সেই এলাকায় 1-এর চেয়ে বড় হয়।

৪ · কনভারজেন্স শর্ত — কেন |g'(x)| < 1

এই আচরণটি কাকতালীয় নয় — এর পেছনে একটি সরল ব্যাখ্যা আছে। যদি x* ফিক্সড পয়েন্ট হয় (x* = g(x*)) এবং e_n = x_n - x* এররকে সংজ্ঞায়িত করি, তাহলে টেইলর সম্প্রসারণ (Taylor expansion) ব্যবহার করে দেখানো যায়:

$$e_{n+1} \approx g'(x^*) \cdot e_n$$

অর্থাৎ প্রতি ধাপে এরর প্রায় g'(x*) গুণ হয়ে যায়। যদি |g'(x*)| < 1 হয়, তাহলে এরর ক্রমাগত ছোট হতে থাকবে (কনভার্জ করবে); যদি |g'(x*)| > 1 হয়, তাহলে এরর ক্রমাগত বড় হতে থাকবে (ডাইভার্জ করবে) — ঠিক যেমনটা উপরের দুটি কোড সেলে সত্যিকারের গণনায় দেখা গেল (0.1440 বনাম 6.9438)।

মূল কথা · Key takeaway

ফিক্সড-পয়েন্ট ইটারেশন একটি সমীকরণকে একাধিকভাবে x = g(x) আকারে লেখা যায় — এবং কোন রূপটি বেছে নেওয়া হচ্ছে তার উপর কনভারজেন্স সম্পূর্ণভাবে নির্ভর করে। বাইসেকশনের বিপরীতে, এখানে কোনো "নিশ্চয়তা" নেই — একটি খারাপ g(x) বেছে নিলে মেথডটি সম্পূর্ণ ব্যর্থ হতে পারে, এমনকি রুটের অত্যন্ত কাছে শুরু করলেও। L07-এর নিউটন-রাফসন মেথড আসলে ফিক্সড-পয়েন্ট ইটারেশনেরই একটি বিশেষ, বুদ্ধিদীপ্ত রূপ — যেখানে g(x) এমনভাবে বেছে নেওয়া হয় যাতে রুটের কাছে g'(x) ≈ 0 হয়।

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

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

প্র ০১ g₁(x) = (x+2)^(1/3) ও g₂(x) = x³ - 2 — দুটোই বীজগাণিতিকভাবে x³ - x - 2 = 0-এর সমতুল্য পুনর্লিখন। তাহলে কেন একটি এত ভালো কাজ করে আর অন্যটি এত খারাপ?

বীজগাণিতিক সমতা কনভারজেন্স নিশ্চিত করে না — শুধু নিশ্চিত করে যে ফিক্সড পয়েন্টটি একই রুট। কনভারজেন্স নির্ধারণ করে g(x)-এর ঢাল রুটের কাছে কেমন, যা সম্পূর্ণ আলাদা একটি বৈশিষ্ট্য। g₁-এর ঘনমূল ফাংশনটি রুটের কাছে "সমতল" (g' ≈ 0.144), কিন্তু g₂-এর ঘনক ফাংশনটি রুটের কাছে "খাড়া" (g' ≈ 6.94) — এই ঢালের পার্থক্যই পার্থক্য তৈরি করে।

প্র ০২ ডাইভার্জিং উদাহরণে আমরা x₀ = 1.4-কে বেছে নিয়েছিলাম, যা রুটের খুব কাছে। এটি কি রুট থেকে দূরে শুরু করলে ডাইভার্জেন্স ঠেকাতে পারত?

না — বরং উল্টো হতো। যেহেতু |g'(x)| = 3x² বড় x-এর জন্য আরও বড় হয়ে যায়, রুট থেকে দূরে শুরু করলে ডাইভার্জেন্স আরও দ্রুত ঘটত। এই মেথডের ব্যর্থতা প্রাথমিক অনুমানের "দূরত্ব" নিয়ে নয় — এটি সম্পূর্ণভাবে g(x)-এর গাণিতিক আকৃতি নিয়ে, যা প্রাথমিক অনুমান পরিবর্তন করে সমাধান করা যায় না।

প্র ০৩ একটি সমীকরণ দেওয়া থাকলে, আপনি কীভাবে আগে থেকেই (কোড না চালিয়ে) বুঝবেন কোন g(x) পুনর্লিখনটি কনভার্জ করবে?

রুটের একটি আনুমানিক অবস্থান জানা থাকলে (এমনকি মোটামুটি অনুমান করেও), g'(x) ক্যালকুলাস দিয়ে বের করে সেই বিন্দুতে এর মান গণনা করা যায় — যদি |g'(x)| < 1 হয়, পুনর্লিখনটি ব্যবহারযোগ্য। ব্যবহারিকভাবে, কোডে প্রথম কয়েকটি ইটারেশন চালিয়ে এরর কমছে না বাড়ছে তা পর্যবেক্ষণ করাও একটি দ্রুত, নির্ভরযোগ্য উপায় — যা আমরা এই পাঠে ঠিক করেছি।

অনুশীলন

  1. চিন্তা করুন: কনভার্জিং উদাহরণে g'(root) ≈ 0.144। যদি প্রথম ইটারেশনের এরর 0.0791 হয়, দ্বিতীয় ইটারেশনের এরর মোটামুটি কত হবে বলে আপনি অনুমান করেন?

    e_{n+1} ≈ g'(x*) · e_n সূত্র অনুযায়ী, 0.0791 × 0.144 ≈ 0.0114 হওয়ার কথা — এবং কোড সেলের প্রকৃত আউটপুটে দ্বিতীয় ইটারেশনের এরর ছিল ০.০১১৪৮, যা এই অনুমানের সাথে প্রায় হুবহু মেলে।

  2. পরীক্ষা করুন: ডাইভার্জিং কোড সেলে x0 = 1.4-কে x0 = 1.521-এ (রুটের প্রায় হুবহু কাছে) বদলে Run চেপে দেখুন এটি ডাইভার্জেন্স থামায় কিনা।

    না থামাবে না — এমনকি x₀ রুটের অত্যন্ত কাছে হলেও, g'(root) ≈ 6.94 হওয়ায় প্রতি ধাপে এরর প্রায় ৭ গুণ বেড়ে যাবে এবং কয়েকটি ইটারেশনের মধ্যেই মান বিস্ফোরিতভাবে বেড়ে যাবে — ঠিক যেমনটা এই পাঠের মূল কথা: প্রাথমিক অনুমান নয়, g(x)-এর ঢালই নির্ধারক।

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

আগের পাঠ
রুট-ফাইন্ডিং সমস্যা ও বাইসেকশন মেথড