বেলম্যান-ফোর্ড এবং এসপিএফএ (Bellman-Ford & SPFA)
একটি মুদ্রা বিনিময় বা কারেন্সি এক্সচেঞ্জ (currency exchange)-এর দামের বোর্ডের কথা চিন্তা করুন। বোর্ডের প্রতিটি সারি আপনাকে এক শহর থেকে অন্য শহরে যাওয়ার বর্তমান খরচ দেখায়। আপনি প্রতিদিন সকালে গিয়ে চেক করেন: "আমি যদি অন্য কোনো শহর হয়ে ঘুরে যাই, তাহলে কি কোনো রুটে আমার খরচ আরও কমবে?" যতক্ষণ পর্যন্ত আর কোনো উন্নতি বা খরচ কমানো সম্ভব না হয়, ততক্ষণ পর্যন্ত আপনি বোর্ডটি প্রতিনিয়ত আপডেট করতে থাকেন।
এটাই হলো মূলত বেলম্যান-ফোর্ড (Bellman-Ford)। ডাইকস্ট্রার (Dijkstra) মতো এটি সব দাম বা দূরত্ব পজিটিভ বলে ধরে নেয় না — এটি অনায়াসেই নেগেটিভ এজ ওয়েট (negative edge weights) সামলাতে পারে। এমনকি এটি এমন অস্বাভাবিক বা প্যাথলজিকাল (pathological) অবস্থা ধরতে পারে যেখানে দাম নেগেটিভ ইনফিনিটির দিকে সর্পিলাকারে বা স্পাইরাল (spiral) হতে থাকে (যাকে একটি নেগেটিভ সাইকেল বা negative cycle বলা হয়)।
অ্যালগরিদম: রিল্যাক্সেশনের V−1 রাউন্ড
শুরুতে: dist[source] = 0 ইনিশিয়ালাইজ বা নির্ধারণ করুন, আর বাকি অন্য সমস্ত দূরত্ব ধরুন = ∞। এরপর এটি V−1 বার পুনরাবৃত্তি বা রিপিট করুন:
গ্রাফের প্রতিটি এজের (u, v, w) জন্য চেক করে দেখুন: যদি dist[u] + w < dist[v] হয়, তবে dist[v] = dist[u] + w দিয়ে আপডেট করুন।
V−1 রাউন্ড কেন? V-ভার্টেক্স গ্রাফের যেকোনো সাধারণ বা সাধারণ পাথ বা পথে সর্বোচ্চ V−1 টি এজ থাকতে পারে। রিল্যাক্সেশনের বা চেক করার প্রতিটি রাউন্ড শর্টেস্ট পাত বা ক্ষুদ্রতম পথের আরও একটি এজকে "সক্রিয় বা অ্যাক্টিভেট" করতে পারে। সুতরাং V−1 রাউন্ডের পর, সমস্ত সহজ শর্টেস্ট পথগুলো সম্পূর্ণরূপে হিসাব করা হয়ে যায় — তবে শর্ত থাকে যে, সেখানে কোনো নেগেটিভ সাইকেল থাকতে পারবে না।
নেগেটিভ সাইকেল বা নেতিবাচক চক্র শনাক্তকরণ
V−1 রাউন্ড শেষ হওয়ার পর, আরো একটি রাউন্ড রান করুন। যদি কোনো দূরত্ব এখনও কমে যায়, এর মানে হলো সোর্স থেকে একটি নেগেটিভ সাইকেল বা নেতিবাচক চক্রে পৌঁছানো যাচ্ছে। কিন্তু কেন? নেগেটিভ সাইকেল নেই এমন গ্রাফে, শর্টেস্ট পাথগুলো সাধারণ পাথ হয় — যা V−1 রাউন্ড আগেই খুঁজে বের করতে পারে। এরপর এর আর কোনো উন্নতি বা খরচ কমার মানে হলো অ্যালগরিদমটি এমন একটি নেগেটিভ সাইকেলের মধ্যে ঘুরপাক খাচ্ছে, যা প্রতিবার চক্কর দেওয়ার সাথে সাথে মোট খরচ কমিয়ে দিচ্ছে।
নেগেটিভ সাইকেলের কারণে শর্টেস্ট পথ খুঁজে পাওয়া অসম্ভব হয়ে দাঁড়ায়: আপনি চাইলে চক্রটিকে বারবার ঘুরিয়ে মোট খরচকে আপনার ইচ্ছেমতো নেগেটিভের দিকে নিয়ে যেতে পারেন। মূলত এই বিশেষ অবস্থাটি শনাক্ত করার জন্যই বেলম্যান-ফোর্ড হলো সবচেয়ে পরিচিত এবং গ্রহণযোগ্য অ্যালগরিদম।
এসপিএফএ (Shortest Path Faster Algorithm বা SPFA)
বেলম্যান-ফোর্ড অ্যালগরিদম তার প্রতিটি রাউন্ডেই এমন কিছু ভার্টেক্সের এজগুলোকে রিল্যাক্স করে বা চেক করে সময় নষ্ট করে যাদের দূরত্ব আসলে পরিবর্তনই হয়নি। SPFA এই সমস্যাটি ঠিক করতে একটি কিউ (queue) ব্যবহার করে: এটি কেবল সেই সমস্ত ভার্টেক্সগুলোকেই নির্দিষ্ট কিউ-তে পাঠায় যাদের দূরত্ব সদ্য উন্নত বা আপডেট হয়েছে, এবং এটি শুধুমাত্র তাদের আউটগোয়িং (outgoing) বা বহির্গামী এজগুলোকেই রিল্যাক্স করে।
বাস্তব ক্ষেত্রে, SPFA কাজ করে \(O(kE)\) সময়ে, যেখানে সাধারণ গ্রাফগুলোর ক্ষেত্রে k ≈ 2 হয় — যা বেলম্যান-ফোর্ডের \(O(VE)\) এর চেয়ে অনেক দ্রুতগতিসম্পন্ন। কিন্তু কিছু প্রতিকূল ইনপুটের (adversarial inputs) কারণে এটি আবারও অবনতি লাভ করে \(O(VE)\)-তে ফিরে যেতে পারে, তাই কম্পিটিটিভ প্রোগ্রামিং (competitive programming) এর ক্ষেত্রে একে একটু সাবধানে ব্যবহার করতে হয়। নেগেটিভ সাইকেল শনাক্তকরণের জন্য, SPFA ট্র্যাক করে দেখতে পারে যে প্রতিটি ভার্টেক্সকে কতবার কিউ-তে বা এনকিউ (enqueue) করা হয়েছে — যদি কোনা একটি ভার্টেক্সকে কিউ-তে V বার এনকিউ করা হয়, তার মানে বুঝতে হবে সেখানে একটি নেগেটিভ সাইকেল বা নেতিবাচক চক্র রয়েছে।
ক্লাসিক একটি প্রয়োগ (Classic Application): কারেন্সি আরবিট্রেজ (Currency Arbitrage)
ধরুন প্রতিটি কারেন্সি বা মুদ্রাকে এক একটি ভার্টেক্স হিসেবে ধরা হলো, আর তাদের এক্সচেঞ্জ রেট বা বিনিময় হারকে −log(rate) ওয়েটসহ ডিরেক্টেড এজ (directed edges) হিসেবে। এই গ্রাফে একটি নেগেটিভ সাইকেল থাকার মানে হলো সাইকেলের চারপাশের বিনিময় হার গুণ করলে এর প্রাপ্ত ফলাফল বা গুণফল ১ (1) এর চেয়ে বেশি হবে — অর্থাৎ এটি একটি লাভজনক আরবিট্রেজ লুপ (arbitrage loop)। বেলম্যান-ফোর্ড ঠিক এই পরিস্থিতিটিই নিখুঁতভাবে শনাক্ত করতে পারে।