পাঠ ০৯ · ৫৭-এর মধ্যে · মডিউল ২
Home / Courses / Design and Analysis of Algorithms / স্পেস কমপ্লেক্সিটি

স্পেস কমপ্লেক্সিটি অ্যানালাইসিস

Space complexity — auxiliary space vs total space
৮ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

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

  • অক্সিলিয়ারি স্পেস ও টোটাল স্পেসের সুনির্দিষ্ট পার্থক্য
  • ইন-প্লেস অ্যালগরিদম কী, এবং কেন এটি প্রায়ই একটি কাম্য বৈশিষ্ট্য
  • sys.getsizeof দিয়ে সত্যিকারের মেমোরি পরিমাপ করা, এবং এর সীমাবদ্ধতা বোঝা
  • $O(1)$ বনাম $O(n)$ অক্সিলিয়ারি স্পেসের পার্থক্য একটি জেনুইন পরিমাপে প্রত্যক্ষ করা

১ · অক্সিলিয়ারি স্পেস বনাম টোটাল স্পেস

একটি অ্যালগরিদমের মোট মেমোরি ব্যবহার দুই ভাগে ভাগ করা যায়:

$$S_{\text{total}}(n) = S_{\text{input}}(n) + S_{\text{aux}}(n)$$

$S_{\text{input}}(n)$ হলো ইনপুট ডেটা স্টোর করতে যতটুকু মেমোরি লাগে (এটি সব অ্যালগরিদমেই সমান — ইনপুট নিজেই দিয়ে দেওয়া থাকে, অ্যালগরিদম এটি তৈরি করে না)। $S_{\text{aux}}(n)$ হলো অক্সিলিয়ারি স্পেস — অ্যালগরিদম চলাকালীন অতিরিক্ত যে মেমোরি ব্যবহার করে (টেম্পোরারি ভ্যারিয়েবল, নতুন ডেটা স্ট্রাকচার, রিকার্শন স্ট্যাক ইত্যাদি)। যেহেতু $S_{\text{input}}(n)$ প্রতিটি অ্যালগরিদমে একই থাকে, তাই তুলনা করার সময় সাধারণত আমরা শুধু $S_{\text{aux}}(n)$ নিয়েই কথা বলি — এটিই প্রকৃত পার্থক্য তৈরি করে।

ইনপুট স্পেস
ইনপুট ডেটা ধরে রাখতে যা লাগে — অ্যালগরিদম-নির্বিশেষে স্থির।
অক্সিলিয়ারি স্পেস
অ্যালগরিদম নিজে চালাতে যে অতিরিক্ত মেমোরি লাগে — এটিই সাধারণত তুলনার বিষয়।
ইন-প্লেস অ্যালগরিদম
$O(1)$ অক্সিলিয়ারি স্পেস ব্যবহার করে — ইনপুটকেই সরাসরি পরিবর্তন করে, নতুন কোনো বড় স্ট্রাকচার তৈরি করে না।
ইনপুট array size n ইন-প্লেস রিভার্সাল swap(left, right) aux = O(1) শুধু left/right ইনডেক্স ইনপুট array size n নতুন লিস্ট তৈরি append() প্রতি এলিমেন্টে aux = O(n) সম্পূর্ণ নতুন n-সাইজ লিস্ট
একই কাজ (রিভার্স করা), একই ইনপুট স্পেস — কিন্তু অক্সিলিয়ারি স্পেসে $O(1)$ বনাম $O(n)$-এর পার্থক্য।

২ · কোড সেল — sys.getsizeof দিয়ে সত্যিকারের পরিমাপ

নিচের কোড সেলে দুটি রিভার্সাল ফাংশন আছে — একটি ইন-প্লেস (দুই-পয়েন্টার swap ব্যবহার করে, দেখুন DSA কোর্স সাধারণ দুই-পয়েন্টার টেকনিকের বিস্তারিত জন্য), আরেকটি সম্পূর্ণ নতুন একটি লিস্ট তৈরি করে। sys.getsizeof ব্যবহার করে অক্সিলিয়ারি স্ট্রাকচারের আকার পরিমাপ করা হয়েছে ক্রমবর্ধমান $n$-এর জন্য।

Python
import sys

def reverse_in_place(arr):
    left, right = 0, len(arr) - 1
    while left < right:
        arr[left], arr[right] = arr[right], arr[left]
        left += 1
        right -= 1
    return arr   # একই লিস্ট অবজেক্ট -- নতুন কোনো n-সাইজ স্ট্রাকচার তৈরি হয়নি

def reverse_new_list(arr):
    reversed_list = []
    for i in range(len(arr) - 1, -1, -1):
        reversed_list.append(arr[i])
    return reversed_list   # সম্পূর্ণ নতুন একটি n-সাইজের লিস্ট

sizes = [100, 1_000, 10_000, 100_000]
print(f"{'n':>8} | {'in-place aux (bytes)':>22} | {'নতুন-লিস্ট aux (bytes)':>22} | {'অনুপাত':>10}")

for n in sizes:
    original = list(range(n))

    arr_copy = original.copy()
    reverse_in_place(arr_copy)
    left_var = 0
    aux_inplace = sys.getsizeof(left_var)   # শুধু একটি int ভ্যারিয়েবলের সাইজ -- n-নির্ভর নয়

    new_list = reverse_new_list(original)
    aux_new = sys.getsizeof(new_list)       # পুরো নতুন লিস্ট অবজেক্টের সাইজ -- n বাড়লে বাড়ে

    assert arr_copy == new_list == list(reversed(original))   # দুটোই সঠিক ফলাফল দেয় তা নিশ্চিত করা

    print(f"{n:>8} | {aux_inplace:>22} | {aux_new:>22} | {aux_new / aux_inplace:>9.1f}x")

print("\nলক্ষ্য করুন: in-place কলামটি n বাড়লেও স্থির থাকে (O(1)),")
print("কিন্তু নতুন-লিস্ট কলামটি n-এর সাথে প্রায় সমানুপাতিকভাবে বাড়ে (O(n))।")

    
গুরুত্বপূর্ণ সতর্কতা: sys.getsizeof একটি শ্যালো পরিমাপ — এটি শুধু অবজেক্টটি নিজে কতটুকু মেমোরি নেয় তা বলে, তার ভেতরে থাকা রেফারেন্স করা অবজেক্টগুলোর (যেমন লিস্টের প্রতিটি integer উপাদান) আকার যোগ করে না। তাছাড়া পাইথন সংস্করণ, প্ল্যাটফর্ম, ও ইন্টারপ্রেটার (CPython, Pyodide-এর মতো ব্রাউজার-ভিত্তিক সংস্করণ) ভেদে প্রকৃত বাইট সংখ্যা কিছুটা ভিন্ন হতে পারে (যেমন সিপাইথনের লিস্ট ওভার-অ্যালোকেশন করে, ফলে প্রকৃত সাইজ ঠিক $n$-এর সমানুপাতিক নাও হতে পারে ছোট পরিসরে)। তাই এই সংখ্যাগুলোকে নিখুঁত বাইট-গণনা হিসেবে নয়, বরং প্রবণতা (in-place ধ্রুবক থাকে, নতুন-লিস্ট $n$-এর সাথে বাড়ে) প্রদর্শনকারী একটি দৃষ্টান্ত হিসেবে দেখা উচিত — যা $O(1)$ বনাম $O(n)$ অক্সিলিয়ারি স্পেসের তাত্ত্বিক দাবিরই একটি বাস্তব প্রতিফলন।
মূল কথা · Key takeaway

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

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

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

প্র ০১ উপরের কোডে aux_inplace পরিমাপ করতে sys.getsizeof(left_var) ব্যবহার করা হয়েছে, যা একটি একক ইন্টিজারের সাইজ দেয়। কিন্তু ফাংশনে তো left এবং right — দুটো ভ্যারিয়েবল আছে। এটি কি পরিমাপকে ভুল করে দেয়?

বড় ভুল নয় — কারণ এখানে মূল পয়েন্টটি হলো এই ভ্যারিয়েবলের সংখ্যা $n$-নির্বিশেষে স্থির (হয় ১টি হোক বা ২টি, উভয়ই একটি ধ্রুবক, $n$ বাড়লে বাড়ে না)। তাই $O(1)$ দাবির জন্য গুরুত্বপূর্ণ বিষয় হলো "$n$-নির্ভর নয়" এই ধর্মটি, ঠিক সংখ্যাটি ১ না ২ তা নয়। একটি আরও সম্পূর্ণ পরিমাপ হতো sys.getsizeof(0) * 2, কিন্তু গ্রোথ-প্যাটার্নের সিদ্ধান্তে (স্থির বনাম n-এর সাথে বৃদ্ধি) কোনো পার্থক্য আসত না।

প্র ০২ reverse_in_place ফাংশনটি নিজেই কি কোনো নতুন মেমোরি লিস্ট আকারে ব্যবহার করে, নাকি একেবারেই না?

একেবারেই নতুন কোনো লিস্ট তৈরি হয় না — এটিই "ইন-প্লেস" শব্দের অর্থ। তবে টেকনিক্যালি, প্রতিটি arr[left], arr[right] = arr[right], arr[left] লাইনে CPython অভ্যন্তরীণভাবে একটি ছোট অস্থায়ী টাপল তৈরি করে সোয়াপ করার জন্য, কিন্তু এই টাপলের আকার $O(1)$ (শুধু দুটি রেফারেন্স ধরে রাখে, $n$-নির্ভর নয়) এবং তা তাৎক্ষণিকভাবে বাতিল হয়ে যায়। তাই সামগ্রিক অক্সিলিয়ারি স্পেস দাবি $O(1)$ হিসেবেই অক্ষুণ্ণ থাকে — এটি একটি ইমপ্লিমেন্টেশন ডিটেইল, অ্যালগরিদমিক দাবি নয়।

প্র ০৩ যদি একটি অ্যালগরিদমের টাইম কমপ্লেক্সিটি $O(n)$ কিন্তু স্পেস কমপ্লেক্সিটি $O(n^2)$ হয় (যেমন একটি সরল কিন্তু অপচয়ী মেমোয়াইজেশন টেবিল), তাহলে সেটিকে কি "ভালো" অ্যালগরিদম বলা যায়?

এটি নির্ভর করে প্রেক্ষাপটের উপর — এখানেই টাইম-স্পেস ট্রেড-অফের ধারণাটি গুরুত্বপূর্ণ হয়ে ওঠে। ছোট $n$-এর জন্য বা যথেষ্ট মেমোরি থাকা সিস্টেমে এটি সম্পূর্ণ গ্রহণযোগ্য হতে পারে। কিন্তু বড় $n$-এ ($n = 10^6$ হলে $n^2 = 10^{12}$) এই স্পেস চাহিদা ব্যবহারিকভাবে অসম্ভব হয়ে যাবে, এমনকি যদি টাইম কমপ্লেক্সিটি চমৎকার হয়। এই কারণেই একটি অ্যালগরিদমকে মূল্যায়ন করার সময় শুধু টাইম নয়, স্পেস কমপ্লেক্সিটিও সবসময় রিপোর্ট করা উচিত — M6-এর ডাইনামিক প্রোগ্রামিং অধ্যায়ে আমরা বারবার দেখব কীভাবে একটি $O(n^2)$-স্পেসের DP টেবিলকে "স্পেস-অপটিমাইজড" করে $O(n)$ বা এমনকি $O(1)$-এ নামিয়ে আনা যায়।

অনুশীলন

  1. চিন্তা করুন: যদি sizes লিস্টে 1_000_000 যোগ করা হয়, তাহলে aux_inplace কলামের মান কী হবে বলে আপনার ধারণা?

    একই থাকবে — $n=100$-এ যা ছিল, $n=1{,}000{,}000$-এও ঠিক তাই থাকবে, কারণ aux_inplace শুধু একটি একক integer ভ্যারিয়েবলের সাইজ পরিমাপ করে, যা $n$-এর মানের উপর নির্ভর করে না। এটিই $O(1)$ অক্সিলিয়ারি স্পেসের প্রকৃত অর্থ — সম্পূর্ণ স্থির, ইনপুট সাইজ নির্বিশেষে।

  2. পরীক্ষা করুন: উপরের কোড সেলে sizes লিস্টে 1_000_000 যোগ করে চালিয়ে দেখুন — aux_new কলামের মান কি $n$-এর সাথে মোটামুটি সমানুপাতিকভাবে বেড়েছে?

    হ্যাঁ — aux_new প্রতিবার $n$ প্রায় ১০ গুণ বাড়লে প্রায় ১০ গুণ বেড়ে যাবে (সিপাইথনের লিস্ট ওভার-অ্যালোকেশনের কারণে অনুপাতটি নিখুঁত ১০.০ নাও হতে পারে, কিন্তু প্রবণতা স্পষ্টভাবে রৈখিক থাকবে) — যেখানে aux_inplace সম্পূর্ণ অপরিবর্তিত থাকবে। এই বৈসাদৃশ্যই $O(1)$ বনাম $O(n)$ অক্সিলিয়ারি স্পেসের মূল শিক্ষা।

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

  • কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ অ্যাসিম্পটোটিক অ্যানালাইসিস, রিকারেন্স, ডিভাইড অ্যান্ড কনকার, গ্রিডি, DP, গ্রাফ অ্যালগরিদম, ব্যাকট্র্যাকিং, স্ট্রিং অ্যালগরিদম, অ্যামর্টাইজড অ্যানালাইসিস, NP-কমপ্লিটনেস ও অ্যাপ্রক্সিমেশন — বাকি পাঠগুলো শীঘ্রই যুক্ত হবে।
  • Data Structures & Algorithms কোর্স সহোদর কোর্স অ্যারে, লিস্ট ও অন্যান্য ডেটা স্ট্রাকচারের মেমোরি লেআউট বিস্তারিত সেই কোর্সে দেখুন।
  • সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git, Theory of Computation, Engineering Economics, Full-Stack Web Frameworks, Mobile App Development, Ethics in Computing & AI Safety, Software Testing & Quality Assurance ও Design and Analysis of Algorithms — সব এক জায়গায়।
আগের পাঠ
বেস্ট, ওয়ার্স্ট ও অ্যাভারেজ কেস অ্যানালাইসিস