ডিভাইড অ্যান্ড কঙ্কার (D&C) প্যাটার্ন (Divide and Conquer Pattern)
ধরুন, আপনার ঘর একদম এলোমেলো অবস্থায় আছে। পুরো ঘর একা পরিষ্কার করার বিষয়টি আপনার কাছে বেশ অপ্রতিরোধ্য বা ওভারহোল্মিং (overwhelming) মনে হচ্ছে — ঠিক যতক্ষণ না পর্যন্ত আপনি বুঝতে পারলেন যে আপনি চাইলে কাজটিকে দুই ভাগে ভাগ করে নিতে পারেন। আপনি ঘরের একটা অর্ধেক পরিষ্কার করেন, আর আপনার ভাই বা বোন অপর অর্ধেকটা পরিষ্কার করে। আচমকা যেই কাজটি একটি বিশাল বড় কাজের মতো মনে হচ্ছিল, সেটি এখন মূলত দুটি ছোট কাজের মতো একই সময়ে হতে থাকছে। যখন আপনারা দুজনেই আপনাদের অর্ধেক ভাগগুলোর কাজ সম্পন্ন করে ফেলবেন, তখন ঠিক পুরো ঘরটি একসাথে পরিষ্কার হয়ে যাবে।
এই বিষয়টাই হলো ডিভাইড অ্যান্ড কঙ্কার (divide and conquer) (ভাগ করুন এবং জয় করুন)। যেকোনো কঠিন একটি সমস্যা (problem) নিন, সেটিকে ছোট ছোট টুকরোতে বা অংশে ভাঙুন, প্রতিটি টুকরোর স্বাধীনভাবে (independently) বা নিজে নিজে সমাধান করুন, এবং সবশেষে সেই ফলাফলগুলোকে একত্রিত করুন। এখানকার আসল ম্যাজিক বা জাদুটা হলো, অতি বিশাল পরিসরে কঠিন মনে হওয়া অনেক সমস্যাই (problems) খুব সহজ বা ট্রিভিয়ালি ইজি (trivially easy) হয়ে ওঠে যখন এর ইনপুটগুলো (input) যথেষ্ট ছোট আকারের হয়।
এর তিনটি প্রধান পর্যায় (The three phases)
ডিভাইড (Divide) বা বিভাজন: ইনপুটটিকে মূলত দুটি বা তার বেশি সংখ্যক ছোট ছোট সাব-প্রোব্লেমে (subproblem) বা উপ-সমস্যায় বিভক্ত করুন। এটি সাধারণত বলতে একটি সাধারণ অ্যারে-কে (array) দুই ভাগে ভাগ করা, এর সার্চ রেঞ্জ (search range) বা অনুসন্ধানের পরিসরকে সংকুচিত বা ন্যারো (narrowing) করা, অথবা কোনো পয়েন্টের বা বিন্দুর সেটকে (set) ভাগ করা বা পার্টিশনিং (partitioning) করাকে বোঝায়।
কঙ্কার (Conquer) বা জয়: প্রতিটি সাব-প্রোব্লেম বা উপ-সমস্যাকে রিকার্সিভলি (recursively) বা পুনরাবৃত্তিমূলকভাবে সমাধান করুন। এর বেস কেস (base case) বা মূল ভিত্তি হলো এমন একটি অবস্থা যখন উপসমস্যাটি এতটাই ছোট বা ক্ষুদ্র হয়ে যায় যে আপনি চাইলে সেটিকে সরাসরি সমাধান করে ফেলতে পারেন — যেমন ধরুন, উদাহরণস্বরূপ: 1 সাইজের বা ১ টি মাত্র উপাদান বিশিষ্ট একটি অ্যারে কিন্তু নিজে থেকেই ইতিমধ্যে সাজানো বা সর্টেড (sorted) থাকে।
কম্বাইন (Combine) বা একত্রীকরণ: তারপর সব উপসংহার বা সাব-সলিউশনগুলোকে (subsolutions) একত্রিত করে আসল সমস্যাটির বা অরিজিনাল প্রোব্লেমের (original problem) জন্য একটি পরিপূর্ণ সমাধানে পরিণত করুন। এটিই হলো সেই ধাপে যেখানে মূলত আসল ইন্টারেস্টিং বা আকর্ষণীয় কাজগুলো হয়ে থাকে — উদাহরণস্বরূপ: মার্জ সর্টে (merge sort) থাকা মার্জ (merge) ধাপের কথা বলা যেতে পারে।
কিছু ক্যানোনিকাল বা গতানুগতিক উদাহরণ (Canonical examples)
মার্জ সর্ট (Merge Sort): অ্যারেকে অর্ধেক করে বা দু'ভাগে বিভক্ত করে, প্রতিটি অর্ধাংশের আলাদাভাবে সর্ট বা সাজানো কাজ সম্পন্ন করা হয় এবং তারপর তাদেরকে মার্জ (merge) বা একত্রিত করে দেয়। যার একত্রীকরণ বা কম্বাইন (Combine) ধাপের খরচ হলো \(O(n)\)। রিকারেন্স (Recurrence): T(n) = 2T(n/2) + \(O(n)\) → \(O(n \log n)\)।
বাইনারি সার্চ (Binary Search): প্রতিটি ধাপে সার্চ স্পেস বা সন্ধানের পরিসরের (search space) পুরো অর্ধেক অংশকেই বাতিল করে দেয়। এর জন্য কোনো কম্বাইন (combine) বা একত্রিত করার ধাপের প্রয়োজন পড়ে না। রিকারেন্স (Recurrence): T(n) = T(n/2) + \(O(1)\) → \(O(\log n)\)।
স্ট্র্যাসেনের ম্যাট্রিক্স মাল্টিপ্লিকেশন বা গুণন (Strassen Matrix Multiplication): ম্যাট্রিক্সগুলোকে চারটি কোয়াড্রান্টে (quadrants) বিভক্ত করে এবং মোট ৮টির পরিবর্তে মূলত ৭টি রিকার্সিভ গুণন (recursive multiplications) ব্যবহার করে। রিকারেন্স (Recurrence): T(n) = 7T(n/2) + \(O(n^2)\) → \(O(n^{2.807})\)।
রিকারেন্স সেট আপ করা (Setting up the recurrence)
প্রতিটি ডিভাইড-অ্যান্ড-কঙ্কার (divide-and-conquer) নির্ভর অ্যালগরিদম ঠিক \(T(n) = a \cdot T(n/b) + f(n)\) এই ফর্ম বা নিয়মের মধ্যে ফিট (fit) হয়ে থাকে, যেখানে \(a\) হলো এর রিকার্সিভ কলগুলোর (recursive calls) সংখ্যা, \(b\) হলো ইনপুটগুলো (input) কল প্রতি ঠিক কতটা পরিমাণ হ্রাস পায় বা ছোট হয়, এবং \(f(n)\) হলো এর ডিভাইড বা বিভক্তকরণ এবং কম্বাইন বা একত্রীকরণের ধাপে ব্যয় হওয়া মূল খরচসমূহ। আর ঠিক এরপরেই মাস্টার থিওরেম (Master Theorem) মূলত \(f(n)\) এর সাথে \(n^{\log_b a}\) কে তুলনা করার মাধ্যমে এই সমস্যাটির সমাধান বের করে।
কখন D&C বা ডিভাইড-অ্যান্ড-কঙ্কার অন্যান্য উপায়গুলোকে ছাড়িয়ে যায় (When D&C beats other approaches)
আপনি ডিভাইড অ্যান্ড কঙ্কার (divide and conquer) এর পথে তখনই হাঁটবেন যখন সাব-প্রোব্লেমগুলো (subproblems) একে অপরের থেকে মূলত স্বাধীন (independent) (অর্থাৎ কোনো শেয়ারড স্টেট বা shared state নেই) হবে এবং এর কম্বাইন বা একত্রীকরণের (combine) ধাপ অনেকটাই সস্তা বা সলভেবল (solvable) হয়ে থাকে। আর যদি দুটি সাব-প্রোব্লেম ওভারল্যাপ (overlap) করে বা একে অপরের সাথে মিলে যায় — অর্থাৎ বারবার একই উত্তর বা সাব-অ্যানসারগুলো (sub-answers) কম্পিউট (compute) করতে থাকে — তাহলে আপনার উচিত ডায়নামিক প্রোগ্রামিং (dynamic programming)-এ সুইচ (switch) করা বা বদলানো। আর এর বদলে যদি কোনো সাধারণ গ্রিডি (greedy) পদ্ধতিটিই সবক্ষেত্রে ভালো কাজ করে যায়, তবে সেটিকে ঠিক তেমন সরল বা সিম্পল করেই রেখে দিন।
মার্জ সর্ট (Merge Sort) — একটি ক্ল্যাসিক (Classic) ডিভাইড অ্যান্ড কঙ্কার
Complexity
ছোট কুইজ
পড়া চালিয়ে যান