পাঠ ৪৩ · ৪৪-এর মধ্যে · মডিউল ৮
Home / Courses / Discrete Mathematics / অ্যালগরিদম তুলনা

অ্যালগরিদম তুলনা — বাস্তব বেঞ্চমার্কিং

Comparing algorithms — empirical benchmarking
৯ মিনিট পড়া মধ্যবর্তী · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

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

  • কেন wall-clock সময়ের বদলে অপারেশন কাউন্ট দিয়ে অ্যালগরিদম তুলনা করা উচিত
  • bubble sort-কে তুলনা-কাউন্টার দিয়ে ইনস্ট্রুমেন্ট করা
  • increasing input size-এ (১০, ২০, ৪০, ৮০) গণনা করে growth trend পর্যবেক্ষণ করা এবং তাত্ত্বিক Θ(n²) প্রেডিকশনের সাথে ক্রস-চেক করা
  • থিওরেটিক্যাল বিশ্লেষণ ও এম্পিরিক্যাল বেঞ্চমার্কিং-এর পার্থক্য বোঝা

১ · কেন Wall-Clock সময় নয়, অপারেশন কাউন্ট

একটি অ্যালগরিদমের বাস্তব গতি সরাসরি সেকেন্ডে পরিমাপ করলে মনে হতে পারে সবচেয়ে সহজ পদ্ধতি — কিন্তু এটি বিভ্রান্তিকর। একই কোড ভিন্ন কম্পিউটারে, ভিন্ন প্রোগ্রামিং ভাষায়, এমনকি একই মেশিনে ভিন্ন মুহূর্তে (background প্রসেসের কারণে) সম্পূর্ণ ভিন্ন সময় নিতে পারে। এর বদলে আমরা মৌলিক অপারেশনের সংখ্যা গোনা — যেমন তুলনা (comparison), সোয়াপ, বা রিকার্সিভ কল — যা হার্ডওয়্যার-নিরপেক্ষ এবং সরাসরি তাত্ত্বিক বিশ্লেষণের সাথে তুলনাযোগ্য।

২ · ওয়ার্কড উদাহরণ — Bubble Sort ইনস্ট্রুমেন্টেড

Bubble sort-এর তত্ত্ব বলে সবচেয়ে খারাপ কেসে (ইতিমধ্যে উল্টো-সাজানো ইনপুট) তুলনা সংখ্যা হলো: $$\text{comparisons} = (n-1) + (n-2) + \cdots + 1 = \frac{n(n-1)}{2}$$ এটি $\Theta(n^2)$ — L39-এর কোয়াড্রেটিক ক্লাস। এই সূত্রটি এম্পিরিক্যালি (বাস্তবে চালিয়ে) যাচাই করি চারটি বিভিন্ন ইনপুট সাইজে — $n=10, 20, 40, 80$।

Python
def bubble_sort_count(arr):
    a = list(arr)
    n = len(a)
    comparisons = 0
    for i in range(n - 1):
        for j in range(n - 1 - i):
            comparisons += 1
            if a[j] > a[j + 1]:
                a[j], a[j + 1] = a[j + 1], a[j]
    return a, comparisons

print(f"{'n':>4} | {'comparisons':>12} | {'n^2':>8} | {'comparisons/n^2':>16}")
for n in [10, 20, 40, 80]:
    arr = list(range(n, 0, -1))   # worst case — সম্পূর্ণ উল্টো ক্রমে সাজানো ইনপুট
    sorted_arr, comps = bubble_sort_count(arr)
    ratio = comps / (n ** 2)
    print(f"{n:>4} | {comps:>12} | {n**2:>8} | {ratio:>16.4f}")

    # সূত্র n(n-1)/2-এর সাথে মিলিয়ে দেখা
    expected = n * (n - 1) // 2
    assert comps == expected, f"n={n}-এ সূত্রের সাথে মিলছে না!"

print("\nসব মান n(n-1)/2 সূত্রের সাথে হুবহু মিলে গেছে — যাচাই সম্পন্ন।")

    
প্যাটার্ন লক্ষ করুন

comparisons/n² অনুপাত $n=10$-এ প্রায় $0.45$, $n=80$-এ প্রায় $0.494$ — n বাড়ার সাথে সাথে এই অনুপাত ধীরে ধীরে $0.5$-এর দিকে এগিয়ে যাচ্ছে (যেহেতু $n(n-1)/2 = n^2/2 - n/2$, এবং $n$ বড় হলে $-n/2$ পদটি তুলনামূলকভাবে নগণ্য হয়ে যায়)। অনুপাতটি প্রায় কনস্ট্যান্ট থাকা-ই হলো $\Theta(n^2)$-এর এম্পিরিক্যাল স্বাক্ষর — যদি এটি ক্রমাগত বাড়তেই থাকত, তাহলে প্রকৃত জটিলতা $n^2$-এর চেয়ে বেশি হতো ($n^3$ বা তার বেশি)।

৩ · তুলনা: Python-এর বিল্ট-ইন sorted()

Python-এর বিল্ট-ইন sorted() Timsort ব্যবহার করে, যার তাত্ত্বিক জটিলতা $\Theta(n\log n)$ (L31-এ merge sort-এর জন্য দেখা ক্লাস)। যেহেতু sorted()-এর অভ্যন্তরীণ তুলনা C ভাষায় বাস্তবায়িত, আমরা সরাসরি এর ভেতরের প্রতিটি তুলনা গুনতে পারি না — কিন্তু ধারণাগতভাবে: $n=80$-এ bubble sort-এর প্রায় $3160$টি তুলনা লাগে (উপরের কোড দেখুন), যেখানে $n\log_2 n \approx 80\times6.3\approx504$ — অর্থাৎ Timsort তাত্ত্বিকভাবে প্রায় ৬ গুণ কম তুলনায় কাজ শেষ করবে। এটাই দেখায় কেন সঠিক জটিলতা ক্লাস বাছাই বাস্তবে সত্যিকারের পার্থক্য তৈরি করে, এমনকি n তুলনামূলক ছোট থাকলেও।

৪ · তাত্ত্বিক বনাম এম্পিরিক্যাল বিশ্লেষণ

পুরো M8 মডিউল জুড়ে আমরা তাত্ত্বিক বিশ্লেষণ শিখেছি — একটি অ্যালগরিদমের কোড না চালিয়েই, শুধু তার গঠন দেখে গাণিতিকভাবে বলে দেওয়া এটি asymptotically ($n\to\infty$) কেমন আচরণ করবে। এম্পিরিক্যাল বেঞ্চমার্কিং (এই পাঠ) ভিন্ন কাজ করে — বাস্তবে অ্যালগরিদম চালিয়ে নিশ্চিত করে তত্ত্ব সঠিক প্রমাণিত হচ্ছে কি না, এবং তত্ত্ব যা লুকিয়ে রাখে (কনস্ট্যান্ট ফ্যাক্টর, ছোট $n$-এ আচরণ) তা প্রকাশ করে। দুটোই প্রয়োজনীয় — তত্ত্ব দিক-নির্দেশনা দেয়, পরিমাপ তা নিশ্চিত করে।

L44-এর ক্যাপস্টোন প্রকল্পে এই দুটো দক্ষতাই একসাথে লাগবে — তাত্ত্বিক Big-O বিবৃতি দেওয়া এবং সেই সাথে বাস্তব সংখ্যা গণনা করে দেখানো।

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

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

প্র ০১ একই কোড দুটি ভিন্ন কম্পিউটারে চালালে wall-clock সময় ভিন্ন হতে পারে কেন — এটি কীভাবে অ্যালগরিদম তুলনাকে অবিশ্বস্ত করে তোলে?

CPU গতি, ক্যাশ সাইজ, ব্যাকগ্রাউন্ড প্রসেস, এমনকি প্রোগ্রামিং ভাষার ইন্টারপ্রিটার/কম্পাইলার ভিন্নতা — এসব কিছুই wall-clock সময়কে প্রভাবিত করে, কিন্তু এগুলোর কোনোটিই অ্যালগরিদমের প্রকৃত দক্ষতা সম্পর্কে কিছু বলে না। একটি O(n²) অ্যালগরিদম একটি অতি দ্রুত মেশিনে একটি O(n log n) অ্যালগরিদমের চেয়ে ছোট $n$-এ দ্রুত মনে হতে পারে, কিন্তু $n$ বড় হলে এই ব্যবধান উল্টে যাবে। অপারেশন কাউন্ট হার্ডওয়্যার থেকে সম্পূর্ণ স্বাধীন, তাই এটি প্রকৃত তুলনার ভিত্তি।

প্র ০২ যদি comparisons/n² অনুপাত n বাড়ার সাথে সাথে ক্রমাগত বাড়তেই থাকত (কনস্ট্যান্টের দিকে না এগিয়ে), এর মানে কী দাঁড়াতো?

এর মানে হতো প্রকৃত জটিলতা $\Theta(n^2)$-এর চেয়ে বেশি — হয়তো $\Theta(n^2 \log n)$ বা $\Theta(n^3)$। যদি অনুপাত একটি কনস্ট্যান্টের কাছাকাছি স্থির থাকে (আমাদের উদাহরণে $0.5$-এর দিকে), সেটাই নিশ্চিত করে হর ($n^2$) সঠিক জটিলতা ক্লাস বেছে নেওয়া হয়েছে। এটি এম্পিরিক্যালি জটিলতা ক্লাস "অনুমান" করার একটি বাস্তব কৌশল — বিভিন্ন হর দিয়ে ভাগ করে দেখা কোনটায় অনুপাত স্থির থাকে।

প্র ০৩ L44-এর ক্যাপস্টোনে একটি গ্রাফ অ্যালগরিদমের দাবি করা জটিলতা O(V+E) — এটি কীভাবে এম্পিরিক্যালি যাচাই করবেন?

এই পাঠের ঠিক একই পদ্ধতি প্রয়োগ করা যাবে — অ্যালগরিদমকে ইনস্ট্রুমেন্ট করে প্রতিটি vertex/edge ভিজিট বা প্রসেস হওয়ার সময় একটি কাউন্টার বাড়ানো, তারপর বিভিন্ন আকারের গ্রাফে ($V, E$ বাড়িয়ে) চালিয়ে দেখা মোট কাউন্ট $V+E$-এর সমানুপাতিক থাকে কি না (যেমন count/(V+E) অনুপাত স্থির থাকা)। ঠিক bubble sort-এর comparisons/n²-এর মতোই এই যাচাই কাজ করবে।

অনুশীলন

  1. পরীক্ষা করুন: কোড সেলের তালিকায় n=160 যোগ করুন এবং Run চাপুন। আগে থেকে অনুমান করুন comparisons/n² অনুপাত কত হবে, তারপর মিলিয়ে দেখুন।

    $n=160$-এ comparisons $=160\times159/2=12{,}720$, এবং $n^2=25{,}600$, তাই অনুপাত $12720/25600 \approx 0.4969$ — আরও কাছাকাছি $0.5$-এর দিকে, নিশ্চিত করে যে প্যাটার্নটি $n$ বাড়ার সাথে সাথে অব্যাহত থাকে।

  2. ডিজাইন করুন: ভাবুন কীভাবে insertion sort-কে একইভাবে তুলনা-সংখ্যা দিয়ে ইনস্ট্রুমেন্ট করবেন এবং bubble sort-এর সাথে তুলনা করবেন একই $n$ মানগুলোতে।

    Insertion sort-এও worst case (উল্টো-সাজানো ইনপুট) $\Theta(n^2)$ — তাই bubble sort-এর মতোই একটি তুলনা-কাউন্টার ভেরিয়েবল যোগ করে প্রতিটি এলিমেন্ট-তুলনার সময় তা বাড়াতে হবে। উভয়ই একই জটিলতা ক্লাসে পড়লেও, তাদের প্রকৃত তুলনা সংখ্যার কনস্ট্যান্ট ফ্যাক্টর ভিন্ন হতে পারে — এম্পিরিক্যাল তুলনাই বলে দেবে কোনটি বাস্তবে দ্রুত।

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

পূর্ববর্তী পাঠ
P বনাম NP ও NP-সম্পূর্ণতা পরিচিতি