গ্রাফপড়তে ৫ মিনিট লাগবে

বেলম্যান-ফোর্ড অ্যালগরিদম (Bellman-Ford Algorithm)

নেগেটিভ রাস্তার শর্টেস্ট পাথ বের করা — এবং ভুল বা নেগেটিভ সাইকেল ধরে ফেলা
time: O(VE)space: O(V)

ডাইকস্ট্রা (Dijkstra) অ্যালগরিদম অনেক ফাস্ট, কিন্তু তার একটাই কড়া নিয়ম আছে: কোনো রাস্তার দূরত্ব বা খরচ নেগেটিভ বা মাইনাস হতে পারবে না। কিন্তু বাস্তব দুনিয়ার কিছু সমস্যা এই নিয়ম মানে না — যেমন কারেন্সির এক্সচেঞ্জ রেট দিয়ে লাভ বা আর্বিট্রেজ (arbitrage) বের করা, নেটওয়ার্কের খরচের মডেল, অথবা এমন কোনো ম্যাপ যেখানে কোনো বিশেষ রাস্তায় গেলে উল্টো আপনার লাভ বা সেভিংস হয়। ঠিক এই জায়গাগুলোতেই বেলম্যান-ফোর্ড (Bellman-Ford) অ্যালগরিদম কাজে আসে।

বেলম্যান-ফোর্ড শুরুর পয়েন্ট থেকে অন্য সব পয়েন্টে যানয়ার সবচেয়ে ছোট বা সস্তা রাস্তা (Shortest Path) বের করতে পারে, এমনকি রাস্তার খরচ নেগেটিভ হলেও। এর চেয়েও বড় কথা, সে একটা ভয়ংকর পরিস্থিতি ধরে ফেলতে পারে: সেটা হলো নেগেটিভ সাইকেল (Negative cycle) — এমন একটা গোলকধাঁধা বা চক্র যার মোট খরচ মাইনাস। এই চক্রে যতবার ঘুরবেন, আপনার মোট খরচ তত কমতে থাকবে (বা লাভ বাড়তে থাকবে), যার ফলে 'সবচেয়ে ছোট রাস্তা' বলে আর কিছু থাকে না, ফলাফল অসীম বা ইনফিনিটি হয়ে যায়।

অ্যালগরিদমটা কীভাবে কাজ করে?

এর আসল ট্রিকটা হলো: V সংখ্যক শহরের ম্যাপে, একটা শহর দুবার ভিজিট না করে সবচেয়ে লম্বা যে রাস্তাটা বানানো সম্ভব, তাতে সর্বোচ্চ V-1 টা রাস্তা বা এজ (edge) থাকতে পারে (এর বেশি হলেই আপনি আগের দেখা কোনো শহরে আবার চলে আসবেন, মানে সাইকেল তৈরি হবে)। তাই এটার সিম্পল রুল হলো: ম্যাপের সব কটি রাস্তাকে ধরে ধরে ঠিক V-1 বার রিল্যাক্স (relax) বা আপডেট করুন। k বার আপডেট করা হলে, গ্যারান্টি দেওয়া যায় যে সর্বোচ্চ k টা রাস্তা পার হয়ে যত শর্টেস্ট পাথ হওয়া সম্ভব, তার সবগুলোই ক্যালকুলেট করা শেষ।

pseudocode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function bellmanFord(nodes, edges, start):
dist = { সব শহর: Infinity বা অসীম }
dist[start] = 0
for i from 1 to V-1: // ঠিক V-1 বার চালান
for [from, to, weight] of edges:
if dist[from] + weight < dist[to]:
dist[to] = dist[from] + weight
// নেগেটিভ সাইকেল আছে কি না চেক করুন
for [from, to, weight] of edges:
if dist[from] + weight < dist[to]:
return 'নেগেটিভ সাইকেল পাওয়া গেছে!'
return dist
Watch Bellman-Ford

← → অ্যারো কি (arrow key) ব্যবহার করুন · উপাদানগুলোতে ক্লিক করুন

নেগেটিভ সাইকেল ধরা

V-1 বার চালানোর পর, সব আসল বা লিগ্যাল শর্টেস্ট পাথ বের হয়ে যানয়ার কথা। এরপরও যদি আমরা অ্যালগরিদমটা আরও একবার (V-তম বার) চালাই, এবং দেখি যে এখনো কারও দূরত্ব কমানো যাচ্ছে, তার একটাই অর্থ হতে পারে—ওই রাস্তাটা এমন কোনো চক্রে বা রিংয়ে ঘুরছে যার মোট ভ্যালু মাইনাস বা নেগেটিভ। আর বেলম্যান-ফোর্ড ঠিক এভাবেই চুরিটা ধরে ফেলে সতর্ক করে দেয়।

বেলম্যান-ফোর্ড বনাম ডাইকস্ট্রা (Dijkstra)

  • ডাইকস্ট্রা (Dijkstra): স্পিড O((V+E) log V), নেগেটিভ রাস্তা সাপোর্ট করে না, প্র্যাকটিক্যালি অনেক ফাস্ট।
  • বেলম্যান-ফোর্ড (Bellman-Ford): স্পিড O(VE), নেগেটিভ রাস্তা ঠিকঠাক হ্যান্ডেল করে এবং নেগেটিভ সাইকেলও ধরতে পারে।

যদি ম্যাপে সব রাস্তার খরচ পজিটিভ বা শূন্য হয়, তবে চোখ বন্ধ করে ডাইকস্ট্রা ব্যবহার করুন। আর যদি কোনো নেগেটিভ রাস্তা থাকে বা নেগেটিভ সাইকেল খোঁজার দরকার পড়ে, তখন বেলম্যান-ফোর্ডের শরণাপন্ন হতে হবে।

বেলম্যান-ফোর্ড প্র্যাকটিক্যালি কোথায় লাগে?

  • কারেন্সি আর্বিট্রেজ বা মুদ্রা বিনিময় করে লাভ বের করা (এখানে নেগেটিভ সাইকেল মানেই হলো আনলিমিটেড ফ্রি টাকা কামানোর সুযোগ)।
  • নেটওয়ার্ক রাউটিং প্রোটোকল (যেমন ইন্টারনেটের RIP প্রোটোকল বেলম্যান-ফোর্ড মেনে চলে)।
  • এমন যেকোনো হিসাব যেখানে রাস্তার ভরের মানে হলো আপেক্ষিক লাভ বা ক্ষতি।
দ্রষ্টব্য: বেলম্যান-ফোর্ডের এই V-1 বার লুপ চালানোর হিসাবটা ম্যাথমেটিক্যালি ১০০% নিখুঁত: ঠিক k টা মাত্র রাস্তা ব্যবহার করে যে শর্টেস্ট পাথ হওয়া সম্ভব, তা ঠিক k বার লুপ চালানোর পরই গ্যারান্টিসহকারে ফাইনাল হয়ে যায়। এখানে অযথা কোনো সময় নষ্ট বা আজগুবি হিসাব হয় না।

Complexity

সময় (Time)
E সংখ্যক সব রাস্তার ওপর লুপ চালাতে হয় V-1 বার
স্লো বা ধীর O(VE)
জায়গা (Space)
সব শহরের দূরত্বের হিসাব রাখার জন্য একটা টেবিল বা অ্যারে
লিনিয়ার O(V)
নেগেটিভ বা ঋনাত্মক রাস্তা
ডাইকস্ট্রা যেটা একদমই করতে পারে না
সাপোর্ট করে —
নেগেটিভ সাইকেল বা চক্র ধরা
V-1 বার চালানোর পর জাস্ট আরও একটা এক্সট্রা লুপ ঘুরিয়েই ধরে ফেলে
হ্যাঁ, ধরে ফেলে —
Challenge

ছোট কুইজ

বেলম্যান-ফোর্ড অ্যালগরিদমে কেন সবগুলো রাস্তাকে ঠিক V-1 বার আপডেট বা রিল্যাক্স করা হয়?

পড়া চালিয়ে যান