ফ্লয়েড-ওয়ার্শল অ্যালগরিদম (Floyd-Warshall)
ধরুন আপনি একটি এয়ারলাইন চালান এবং আপনি প্রতিটি শহরের জোড়ার (pair) মধ্যে সবচেয়ে সস্তা ভাড়া জানতে চান। আপনি প্রতিটি শহর থেকে ডাইকস্ট্রার অ্যালগরিদম চালাতে পারেন, তবে এর চেয়ে আরও একটি সুন্দর উপায় আছে: প্রতিটি শহরের জন্য জিজ্ঞাসা করুন, "K শহরের ওপর দিয়ে উড়ে গেলে কি কোনো ট্রিপ সস্তা হয়?"
সরাসরি ফ্লাইটের দাম দিয়ে শুরু করুন। তারপর জিজ্ঞাসা করুন: "১ নম্বর শহরের ওপর দিয়ে গেলে কি কোনো ট্রিপ সস্তা হয়?" দামগুলো আপডেট করুন। তারপর: "২ নম্বর শহরের ওপর দিয়ে গেলে কি কোনো ট্রিপ সস্তা হয়?" আবার আপডেট করুন। প্রতিটি শহরকে একটি সম্ভাব্য লেওভার (layover) বা যাত্রাবিরতি হিসেবে বিবেচনা করার পর, আপনার প্রাইস টেবিলের প্রতিটি এন্ট্রিই হবে প্রকৃত সবচেয়ে সস্তা ভাড়া।
এটাই হলো ফ্লয়েড-ওয়ার্শল (Floyd-Warshall) — একটি চমৎকার সাধারণ ডাইনামিক প্রোগ্রামিং (DP) ধারণার ওপর নির্মিত শর্টেস্ট-পাথ অ্যালগরিদম যা সব জোড়ার (all-pairs) জন্য কাজ করে।
ডিপি ফর্মুলেশন (The DP Formulation)
dist[i][j] কে i থেকে j পর্যন্ত সবচেয়ে ছোট পথ হিসাবে ধরে নিন। প্রাথমিকভাবে:
- যদি সরাসরি কোনো এজ বা পথ থাকে, তবে
dist[i][j] = weight(i,j) dist[i][i] = 0- অন্যথায়
dist[i][j] = ∞
মূল রিকারেন্স (recurrence) — 1 থেকে V পর্যন্ত প্রতিটি মধ্যবর্তী ভার্টেক্স (intermediate vertex) k এর জন্য লুপ চালান:
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
সহজ কথায়: "k এর ওপর দিয়ে i থেকে j তে পৌঁছানো কি আমাদের এপর্যন্ত পাওয়া যেকোনো রুটের চেয়ে সস্তা?" আউটার লুপ (outer loop) k এর সমস্ত V মান শেষ করার পর, প্রতিটি জোড়া (i, j) গ্লোবালি অপ্টিমাল (globally optimal) বা সর্বোত্তম উত্তর ধারণ করে।
কেন ইন-প্লেস আপডেট করা নিরাপদ (Why In-Place Updates Are Safe)
3D ফর্মুলেশন dp[k][i][j] কে একটি 2D অ্যারে-তে সংকুচিত করা যায়। আপনি কেন ইন-প্লেস আপডেট করতে পারেন? যখন মধ্যবর্তী ভার্টেক্স k প্রসেস করা হয়, তখন dist[i][k] এবং dist[k][j] এর মানগুলো k-তম ইটারেশনে পরিবর্তন হয় না — এগুলো এমন পথগুলোকে নির্দেশ করে যা মধ্যবর্তী হিসেবে {1..k−1} ব্যবহার করে, যা আগে থেকেই অপ্টিমাল বা সর্বোত্তম এবং এগুলো মধ্যবর্তী হিসেবে k-কে ব্যবহার করে না (k-এ যাওয়ার জন্য k-এর ভেতর দিয়ে গেলে কেবল খরচই বাড়বে)। তাই এই মানগুলো ব্যবহার করে ইন-প্লেস dist[i][j] আপডেট করা সবসময়ই সঠিক।
নেগেটিভ সাইকেল শনাক্ত করা (Detecting Negative Cycles)
ফ্লয়েড-ওয়ার্শল চালানোর পর, ডায়াগোনাল বা কর্ণ বরাবর চেক করুন। যদি কোনো ভার্টেক্স i এর জন্য dist[i][i] < 0 হয়, তবে i এর ভেতর দিয়ে যাওয়া একটি নেগেটিভ সাইকেল রয়েছে। i থেকে i তে যাওয়ার একটি তুচ্ছ বা খালি পথের খরচ 0; যদি এটি নেগেটিভ হয়ে যায়, এর মানে অ্যালগরিদমটি এমন একটি সাইকেল পেয়েছে যার মোট ওয়েট নেগেটিভ। নেগেটিভ সাইকেলের ভেতর দিয়ে যায় এমন যেকোনো ক্ষুদ্রতম পথ আনডিফাইন্ড (undefined) বা অসংজ্ঞায়িত (অসীমভাবে সস্তা)।
ট্রানজিটিভ ক্লোজার (Transitive Closure)
min/+ এর বদলে OR/AND (বুলিয়ান মান ব্যবহার করে) প্রতিস্থাপন করে প্রতিটি জোড়া (i, j) একে অপরের সাথে যুক্ত কি না তা গণনা করুন। এই ট্রানজিটিভ ক্লোজার (transitive closure) পদ্ধতিটি O(V³) সময়ে কাজ করে এবং এক চক্করেই সব জোড়ার জন্য রিচেবিলিটি কোয়েরির (reachability queries) উত্তর দেয়।
কখন ফ্লয়েড-ওয়ার্শল বনাম ডাইকস্ট্রা ব্যবহার করবেন
একটি ডেনস গ্রাফে (dense graph) (E ≈ V²) সমস্ত জোড়ার শর্টেস্ট পাথে বা নেগেটিভ ওয়েট থাকলে, সাধারণত ফ্লয়েড-ওয়ার্শলের O(V³) বড় সুবিধা দেয়। শুধুমাত্র পজিটিভ ওয়েট-যুক্ত একটি স্পার্স গ্রাফে (sparse graph), প্রতিটি সোর্স থেকে ডাইকস্ট্রা চালালে তা O(V · (V+E) log V) সময় নেয়, যা অনেক ভালো হতে পারে। সাধারণত যখন E ≪ V² হয় তখন ডাইকস্ট্রা ভালো কাজ করে।