টাস্ক শিডিউলিং (Task Scheduling)
ধরুন রবিবার রাত, আর আপনার কাছে একগাদা অ্যাসাইনমেন্ট বা বাড়ির কাজ জমা পড়েছে। প্রতিটি ডেডলাইনের মধ্যে জমা দিতে পারলে আপনি পয়েন্ট পাবেন, এবং প্রতিটি কাজের জন্য ঠিক এক ঘণ্টা সময় লাগবে। আপনি একবারে কেবল একটি কাজই করতে পারবেন। আপনি কোন কাজগুলো আগে করবেন?
আপনি যদি বুদ্ধিমান হন: তবে সবচেয়ে বেশি লাভজনক কাজটি আগে বেছে নেবেন। সেটির ডেডলাইনের ঠিক আগের সর্বশেষ ফাঁকা স্লটে (latest available slot) কাজ শুরু করার রুটিন করবেন (যাতে আপনি অন্যান্য কাজের জন্য আগের স্লটগুলো ফাঁকা রাখতে পারেন)। যে কাজের জন্য আর কোনো ফাঁকা স্লট অবশিষ্ট নেই, সেটি বাদ দেবেন।
এক প্যারায় বলতে গেলে, এটিই হলো ডেডলাইনসহ টাস্ক শিডিউলিং। এর প্রধান কৌশলটি হলো "সর্বশেষ ফাঁকা স্লটটি খুঁজে বের করার" ধাপটিকে দ্রুত করা।
সমস্যাটির আনুষ্ঠানিক বর্ণনা (Formalising the Problem)
আপনার কাছে n সংখ্যক ইউনিট-টাইম টাস্ক (unit-time tasks) বা এক-ঘণ্টার কাজ আছে (প্রতিটি কাজে ঠিক ১টি টাইম স্লট লাগে)। টাস্ক i-এর জন্য আছে:
• ডেডলাইন d_i — অবশ্যই টাইম স্লট 1 থেকে d_i এর মধ্যে সম্পন্ন করতে হবে
• প্রফিট বা লাভ p_i — কেবল ডেডলাইনের মধ্যে শেষ করলেই লাভ হবে
আপনি প্রতি স্লটে সর্বাধিক একটি কাজ সম্পাদন করতে পারবেন। উদ্দেশ্য: মোট লাভ সর্বোচ্চ করার জন্য কোন কাজগুলো চালাবেন তা বেছে নেওয়া।
গ্রিডি পদ্ধতি (The Greedy Strategy)
১. টাস্কগুলোকে লাভের (profit) ক্রমানুসারে বড় থেকে ছোটতে সাজান (descending order)।
২. প্রতিটি টাস্কের জন্য (লাভের ক্রমানুসারে), এর ডেডলাইনের সমান বা ঠিক আগের সর্বশেষ ফাঁকা স্লটটি (latest free slot) খুঁজে বের করুন।
৩. যদি একটি ফাঁকা স্লট পাওয়া যায়, তবে কাজটিকে সেই স্লটে বরাদ্দ করুন এবং স্লটটিকে 'ব্যবহৃত (used)' হিসেবে চিহ্নিত করুন।
৪. যদি কোনো ফাঁকা স্লট না থাকে, তবে কাজটি এড়িয়ে যান (skip)।
সবচেয়ে সর্বশেষ (latest) উপলব্ধ স্লটে (সবচেয়ে আগেরটির পরিবর্তে) বরাদ্দ করাটা ইচ্ছাকৃতভাবে করা হয় — এর ফলে ভবিষ্যতে এমন অধিক লাভের কাজগুলো (high-profit tasks) সহজে করা যায় যেগুলোর ডেডলাইন হয়তো খুব কাছাকাছি।
ইউনিয়ন-ফাইন্ডের (Union-Find) সাহায্যে দ্রুত স্লট খোঁজা
সাধারণ পদ্ধতি (naive approach): প্রতিটি টাস্কের জন্য, এর শুরুর ডেডলাইন থেকে পেছনের দিকে খুঁজতে থাকুন যতক্ষণ না আপনি একটি ফাঁকা স্লট পাচ্ছেন। এর ফলে প্রতি টাস্কের জন্য O(n) সময় লাগে, এবং মোট সময় লাগে O(n²) — যা অনেক ধীর।
বুদ্ধিদীপ্ত পদ্ধতি (smart approach): একটি ইউনিয়ন-ফাইন্ড বা Union-Find (ডিসজয়েন্ট সেন্ট বা disjoint set) স্ট্রাকচার বজায় রাখুন, যেখানে parent[t] সর্বশেষ ফাঁকা স্লটটি (≤ t) নির্দেশ করে। যখন স্লট t ব্যবহার করা সম্পন্ন হয়, তখন union(t, t−1) করে দিন, যাতে ভবিষ্যতে ≤ t খোঁজার জন্য সিস্টেমটি স্বয়ংক্রিয়ভাবে পরবর্তী ফাঁকা স্লটে চলে যেতে পারে। প্রতিটি খোঁজার কাজের জন্য প্রায়-O(1) অ্যামোরটিজড বা গড় (amortized) সময় লাগে (inverse Ackermann function)। মোট সময়: সর্ট করার জন্য O(n log n), শিডিউলিংয়ের জন্য O(nα(n)) — সর্ট করার ধাপটি সবচেয়ে বেশি সময় নেয়।
গ্রিডি পদ্ধতি কেন সঠিক — ম্যাট্রয়েড তত্ত্ব (Matroid Theory)
একাধিক কাজ যেগুলো নির্দিষ্ট সময়ে সম্পন্ন করা যায় (অর্থাৎ ফিজিবল টাস্ক সেটের কালেকশন) মিলে একটি ম্যাট্রয়েড (matroid) তৈরি করে। যেকোনো ওয়েটেড বা ওজনের ম্যাট্রয়েডের ক্ষেত্রে, গ্রিডি অ্যালগরিদম — ওজনের ক্রমানুসারে বড় থেকে ছোটতে সাজিয়ে, একটি সেট যদি স্বাধীন (independent) থাকে তবে তাতে নতুন কোনো উপাদান যোগ করা — সবচেয়ে বেশি ওজনের স্বাধীন সেট (maximum-weight independent set) খুঁজে বের করে। এখানে গ্রিডি পদ্ধতি কাজ করার পেছনে এটাই হলো গভীর তাত্ত্বিক কারণ: কারণ এর ফিজিবিলিটির (feasibility) ধরন হুবহু ম্যাট্রয়েড ইনডিপেনডেন্সের কাঠামোর মতো।
ফিজিবিলিটির মূল যাচাইকরণ (Key Feasibility Check)
একটি কাজের সেট S তখনই ফিজিবল বা সম্ভব হয় যদি ও কেবল যদি প্রতিটি সময় t এর জন্য, S এ থাকা কাজগুলোর ডেডলাইন ≤ t হয় এবং সেগুলোর সংখ্যা সর্বোচ্চ t হয়। সহজ কথায়: কোনো নির্দিষ্ট ডেডলাইনের মধ্যে যে কয়টি টাইম স্লট আছে, তার চেয়ে বেশি কাজ আপনি করতে পারবেন না। এই শর্তটি ইউনিয়ন-ফাইন্ড ব্যবহার করে বেশ দক্ষতার সাথে যাচাই করা সম্ভব।