মিডিয়ান অফ মিডিয়ানস (Median of Medians)
ধরুন আপনার ক্লাসে খুব পক্ষপাতিত্বহীনভাবে একটি স্পোর্টস দলের ক্যাপ্টেন বেছে নেওয়া দরকার। সবাই একসাথে ভোট না দিয়ে (কারণ এতে কারচুপি হতে পারে), আপনারা একটু ভিন্ন পদ্ধতি অবলম্বন করলেন: সবাইকে ৫ জনের ছোট ছোট দলে ভাগ করে দিলেন, তারপর প্রতিটি দলকে তাদের নিজেদের লিডার বা ক্যাপ্টেন বেছে নিতে বললেন। এরপরে সব দলের লিডাররা নিজেদের মধ্যে ভোট দিয়ে আরেকজনকে নির্বাচন করলো। এই পদ্ধতিতে যে জিতবে, সে সবার একজন ভালো প্রতিনিধি হবে।
মিডিয়ান অফ মিডিয়ানস (Median of Medians) ঠিক এই কাজটিই করে — এবং একই যুক্তিতে কাজ করে। এটি এমন একটি পিভট উপাদান (pivot element) বেছে নেয় যা অ্যারেটিকে এমনভাবে ভাগ করার নিশ্চয়তা দেয় যেন কোনো হ্যাকার বা অ্যাডভারসারি (adversary) এটিকে বোকা বানিয়ে \(O(n^2)\) কমপ্লেক্সিটিতে নিয়ে যেতে না পারে।
কুইক-সিলেক্টের (QuickSelect) সমস্যা
র্যান্ডমাইজড কুইক-সিলেক্ট বা দৈবচয়নভিত্তিক কুইক-সিলেক্ট বাস্তবে চমৎকার কাজ করে — এটি প্রায় শতভাগ ক্ষেত্রেই প্রত্যাশিত \(O(n)\) সময়ে সম্পন্ন হয়। কিন্তু যদি কেউ ইচ্ছে করে এমন একটি ইনপুট দেয় যা আপনার অ্যালগরিদমকে বিপর্যস্ত করতে পারে, তখন আপনার র্যান্ডম পিভট সবসময়ই বাজে হতে পারে, এবং আপনাকে \(O(n^2)\) এ ঠেলে দিতে পারে। সিকিউরিটি-ক্রিটিকাল কোডে (যেখানে নিরাপত্তা খুবই গুরুত্বপূর্ণ), অথবা যেখানে আপনার প্রমাণযোগ্য (provable) ওয়ার্স্ট-কেস বা সবচেয়ে খারাপ অবস্থার গ্যারান্টি দরকার, সেখানে প্রামাণ্য এবং সুনিশ্চিত (deterministic) কিছু দরকার হয়।
অ্যালগরিদমের ধাপে ধাপে প্রক্রিয়া
ধাপ ১ — ৫ জনের দলে ভাগ করা: n সংখ্যক উপাদানকে ঠিক ৫টি উপাদানের একেকটি দলে ভাগ করুন (তবে সর্বশেষ দলটিতে একটু কম উপাদান থাকতে পারে)। ৫টি উপাদানের দল হলো সবচেয়ে সেরা মাপ — এটি এতই ছোট যে সাথে সাথে সর্ট করা যায়, আবার এটি এত বড় যে মানের ক্ষেত্রে একটি গ্যারান্টিও পাওয়া যায়।
ধাপ ২ — প্রতিটি দল সর্ট করুন এবং এদের মধ্যবর্তী বা মিডিয়ানটি নিন: প্রতিটি ৫ উপাদানের দলকে ইনসার্শন সর্ট (insertion sort) দিয়ে সর্ট করুন (যার জন্য সর্বোচ্চ ৭ বার তুলনা করতে হয়)। এরপর মাঝখানের উপাদানটি নিন। এবার আপনার কাছে ⌈n/5⌉ সংখ্যক দলের মিডিয়ান বা মধ্যবর্তী মান রয়েছে।
ধাপ ৩ — এই মিডিয়ানগুলোর মধ্য থেকে রিকার্সিভলি (recursively) মিডিয়ান বের করুন: পুরো অ্যালগরিদমটিকে এখন শুধু এই ⌈n/5⌉ মিডিয়ানগুলোর ওপর রিকার্সিভলি প্রয়োগ করুন। এর ফলাফলই হলো মিডিয়ানগুলোর মিডিয়ান (median of medians) — চলুন এর নাম দিই M।
ধাপ ৪ — M-কে আপনার পিভট হিসেবে ব্যবহার করুন: এবার M-কে পিভট ধরে মূল অ্যারেকে পার্টিশন (partition) বা ভাগ করুন। যেহেতু M একটি ভালো পিভট (যা সুনিশ্চিতভাবে ৩০-৭০ অনুপাতে ভাগ করে), তাই আপনি শুধু সেই দিকেই রিকার্শন প্রয়োগ করুন যেদিকে আপনার খোঁজা উপাদানটি (target element) থাকার কথা।
কেন M একটি ভালো পিভট?
M হলো ⌈n/5⌉ সংখ্যক মানের মিডিয়ান। এই মানগুলোর অন্তত অর্ধেক হলো M এর চেয়ে বড় বা সমান (≥ M)। এই মানগুলোর প্রত্যেকেই আবার ৫ উপাদানের একেকটি দলের মিডিয়ান ছিল, অর্থাৎ ঐ দলে এদের ছাড়াও কমপক্ষে আরও ২টি উপাদান ছিল যারা এদের চেয়ে বড় বা সমান (≥ M)। তার মানে, পুরো অ্যারের ওই দলগুলো থেকেই কমপক্ষে (n/5)/2 × 2 = n/5 সংখ্যক উপাদান M এর চেয়ে বড় বা সমান (≥ M) — এর সাথে M তো আছেই। খুব ভালোভাবে হিসাব করলে দেখা যায়, কমপক্ষে 3n/10 সংখ্যক উপাদান M এর প্রতিটি পাশে থাকাটা সুনিশ্চিত। ফলে এই পার্টিশন কখনও 70%-30% এর চেয়ে বেশি বৈষম্যমূলক হয় না বা এটি কখনোই পুরোপুরি একপেশে হয় না।
এর রিকারেন্স (The recurrence)
T(n) = T(n/5) + T(7n/10) + \(O(n)\)। এখানে T(n/5) হলো মিডিয়ানগুলোর মিডিয়ান রিকার্সিভলি বের করার জন্য। T(7n/10) হলো বড় অংশের ওপর পার্টিশন রিকার্শনের জন্য। আর \(O(n)\) হলো দল তৈরি, দলগুলোকে সর্ট করা এবং পার্টিশন করার জন্য। এটি সমাধান করলে আমরা পাই T(n) = \(O(n)\)।