من شوارع فلسطين إلى عالم الخوارزميات: دليلك العملي لحل مشكلة مسارات التوصيل مع Dijkstra و A*

يا جماعة الخير، السلام عليكم ورحمة الله.

بتذكر قبل كم سنة، كان لي صديق عزيز اسمه “خالد” فتح مشروع صغير لتوصيل طلبات المطاعم في مدينته. كان الشب شعلة نشاط وحماس، وبدأ بسيارة واحدة وكم سائق. في البداية، كانت الأمور “ماشية بالبركة”، السائق ياخذ الطلب ويتوكل على الله، يمشي على معرفته بالطرق أو على جوجل مابس. لكن لما كبر الشغل وصار عنده أسطول صغير من 5 سيارات ومئات الطلبات في اليوم، بدأت المشاكل تظهر.

إجاني خالد في يوم مهموم، وحكالي: “يا أبو عمر، والله رح أفلس! مصاريف البنزين بتاكل كل الربح، والعملاء بشتكوا من التأخير، والسواقين ضايعين في الشوارع”. قعدنا مع بعض على فنجان قهوة، وفتحت اللابتوب وفرجيته خريطة المدينة، وحكيتله: “يا خال، القصة مش قصة سواق شاطر أو مش شاطر، القصة قصة علم. هاي الخريطة اللي شايفها، إحنا كـ مبرمجين ما بنشوفها شوارع وبيوت، إحنا بنشوفها ‘غراف’ (Graph) ونقاط وخطوط… وهون ببدأ السحر”.

هذيك الجلسة كانت بداية رحلة طويلة حوّلنا فيها فوضى التوصيل عند خالد لنظام دقيق وفعّال. واليوم، بدي أشارككم جزء من هاي الرحلة، ونحكي عن كيف بنحل مشكلة مسارات التوصيل باستخدام أقوى الأدوات في جعبتنا: خوارزميات Dijkstra و A*.

أ) فهم المشكلة: ليش الطريق الغلط بكلف كثير؟

تخيل إنك صاحب شركة توصيل. كل يوم عندك آلاف الطلبات لازم توصل لأصحابها. التحدي مش بس في توصيل الطلب، التحدي الحقيقي هو في الكفاءة. كل دقيقة تأخير وكل لتر بنزين زيادة هو خسارة مباشرة من جيبك. المشكلة إلها وجهين رئيسيين:

  • أقصر مسار بين نقطتين (Point-to-Point): سائق في المستودع (أ) ولازم يوصل طلب لبيت عميل (ب). ما هو أسرع طريق؟ هل هو أقصر طريق بالكيلومترات؟ مش بالضرورة! يمكن يكون في طريق أطول بس ما فيه أزمة سير، فبوصل أسرع.
  • مسار يمر بعدة نقاط (Multi-Stop Route): سائق عنده 10 طلبات لازم يوصلها. بأي ترتيب لازم يزور العملاء عشان يكون إجمالي المسار هو الأقصر والأسرع؟ هاي مشكلة أعقد بكثير اسمها “مشكلة البائع المتجول” (Traveling Salesman Problem).

عشان نعقّد الأمور أكثر، الطرق مش كلها زي بعض. كل طريق له “وزن” (Weight) مختلف. هذا الوزن ممكن يكون:

  • المسافة: بالكيلومتر أو الميل.
  • الزمن: بالدقائق، وهذا الوزن متغير حسب وقت الذروة والحوادث.
  • التكلفة: بعض الطرق فيها رسوم عبور.
  • نوع الطريق: طريق سريع غير طريق داخلي ضيق.

اختيار المسار الغلط يعني ببساطة: عميل غاضب، وتكاليف تشغيل أعلى، وسمعة سيئة للشركة.

ب) مفهوم الحل: كيف نترجم الخريطة للغة الكمبيوتر؟

الحل الحقيقي، يا جماعة، ببدأ لما نتوقف عن التفكير في الخريطة كصورة، ونبدأ نفكر فيها كـ “غراف” أو شبكة بيانية. الشغلة مش سحر، هي مجرد تمثيل رياضي منظم للعالم الحقيقي.

الغراف (Graph) هو ببساطة مجموعة من النقاط (Nodes) متصلة ببعضها عن طريق خطوط (Edges).

  • النقاط (Nodes/Vertices): هي التقاطعات، أو مواقع العملاء، أو المستودعات، أو أي مكان مهم على الخريطة.
  • الخطوط (Edges): هي الشوارع اللي بتربط بين هاي النقاط.
  • الأوزان (Weights): هي “تكلفة” السير على هذا الشارع (زمن، مسافة، …الخ).

لما تصير الخريطة عنا بهاي الصورة، بنقدر نطبق عليها خوارزميات عبقرية لإيجاد أقصر المسارات. أشهر وأهم هاي الخوارزميات هما:

1. خوارزمية دايكسترا (Dijkstra’s Algorithm)

خوارزمية دقيقة ومضمونة، بتشتغل على أي غراف أوزانه غير سالبة (وهو الحال في خرائط الطرق، ما في طريق زمنه بالسالب!). فكرتها الأساسية هي أنها بتبدأ من نقطة البداية، وبتصير “تكتشف” الطرق المحيطة فيها بشكل طماع (Greedy). في كل خطوة، بتختار الطريق الأقصر من بين كل الطرق اللي لسا ما زارتها، وبتحدّث المسافات لجيرانها. بتضل تكرر هاي العملية لحد ما توصل لكل النقاط الممكنة أو تلاقي الهدف.

دايكسترا بتضمن لك 100% إنها رح تلاقي المسار الأقصر حسابياً. لكن مشكلتها إنها “عمياء”، يعني بتبحث في كل الاتجاهات بنفس الأهمية، وهذا ممكن يكون بطيء في الخرائط الكبيرة جداً.

2. خوارزمية A* (A-Star)

هاي الخوارزمية هي النسخة الذكية والمحسّنة من دايكسترا. بتعمل نفس شغل دايكسترا، لكن مع إضافة مكون سحري اسمه “الدالة الحدسية” أو Heuristic Function.

الـ Heuristic هو تخمين ذكي للمسافة المتبقية للوصول للهدف. أشهر مثال هو “المسافة الجوية” أو الخط المستقيم بين النقطة الحالية ونقطة النهاية. فكرة A* هي إنها في كل خطوة ما بتختار بس الطريق الأقصر لغاية الآن (زي دايكسترا)، لكنها بتوازن بين شغلتين:

التكلفة الكلية = التكلفة الفعلية من البداية + التكلفة المُخَمَّنة (Heuristic) للنهاية

هذا التخمين بيخلي الخوارزمية “تفضل” التحرك في الاتجاه العام للهدف، بدل ما تضيع وقتها في استكشاف طرق بعيدة وواضح إنها مش رح توصل للحل بسرعة. النتيجة؟ سرعة أعلى بكثير في إيجاد الحل في معظم الحالات العملية.

ج) مثال عملي: من المستودع لخمسة عملاء

خلونا نطبق الحكي النظري على سيناريو عملي. هدفنا الأول هو بناء الأداة الأساسية: حساب أسرع طريق بين أي نقطتين. بعد هيك، بنقدر نستخدمها لحل مشكلة توصيل الطلبات المتعددة.

الخطوة 1: بناء الغراف (Graph)

أول وأهم خطوة هي الحصول على بيانات الخريطة وتحويلها لغراف. من أفضل المصادر المجانية هي OpenStreetMap (OSM). هي ويكيبيديا الخرائط، مشروع عالمي مفتوح المصدر بيحتوي على بيانات تفصيلية للشوارع والتقاطعات حول العالم.

في بايثون، في مكتبات رائعة بتسهل هاي المهمة مثل osmnx و networkx.

نصيحة من الخبير: لا تحاول تبني محلل بيانات OSM من الصفر! استغل الأدوات الموجودة. مكتبة osmnx بتخليك تحمل شبكة الطرق لأي مدينة في العالم بسطر كود واحد.

لتمثيل الغراف، بنستخدم عادةً قائمة المجاورة (Adjacency List)، وهي عبارة عن قاموس (dictionary) مفاتيحه هي النقاط، وقيمة كل مفتاح هي قائمة بالنقاط المجاورة والتكلفة للوصول إليها.


# مثال بسيط جداً على شكل الغراف (Adjacency List)
# 'A': {'B': 5, 'C': 10} تعني: من النقطة A يمكن الوصول لـ B بتكلفة 5 ولـ C بتكلفة 10

graph = {
    'Warehouse': {'A': 10, 'B': 20},
    'A': {'Warehouse': 10, 'C': 5, 'D': 15},
    'B': {'Warehouse': 20, 'D': 10},
    'C': {'A': 5, 'E': 5},
    'D': {'A': 15, 'B': 10, 'E': 8, 'F': 20},
    'E': {'C': 5, 'D': 8, 'F': 7},
    'F': {'D': 20, 'E': 7}
}

# العملاء موجودين في النقاط: C, D, E, F
# المستودع: Warehouse

الخطوة 2: تطبيق خوارزمية دايكسترا

بعد ما بنينا الغراف، بنقدر نطبق الخوارزمية. هاي نسخة مبسطة من دايكسترا باستخدام بايثون ومكتبة heapq (اللي بتوفر لنا Priority Queue بكفاءة عالية).


import heapq

def dijkstra(graph, start_node):
    # قاموس لتخزين أقصر مسافة معروفة من البداية لكل نقطة
    distances = {node: float('infinity') for node in graph}
    distances[start_node] = 0
    
    # قاموس لتتبع المسار
    previous_nodes = {node: None for node in graph}
    
    # (distance, node)
    priority_queue = [(0, start_node)]
    
    while priority_queue:
        # خذ النقطة ذات المسافة الأقل من الـ priority queue
        current_distance, current_node = heapq.heappop(priority_queue)
        
        # إذا وجدنا طريق أقصر لهذه النقطة من قبل، تجاهلها
        if current_distance > distances[current_node]:
            continue
            
        # استكشف جيران النقطة الحالية
        for neighbor, weight in graph[current_node].items():
            distance = current_distance + weight
            
            # إذا وجدنا مسارًا جديدًا أقصر إلى الجار، قم بتحديثه
            if distance  '.join(shortest_path_to_F)}")
# الناتج المتوقع: Warehouse -> A -> C -> E -> F (كمثال، يعتمد على الأوزان)

الخطوة 3: التوسع لمشكلة الطلبات المتعددة (VRP/TSP)

الآن صار عنا القدرة نحسب المسافة بين أي نقطتين (مثلاً المستودع ↔ عميل C، عميل C ↔ عميل D، وهكذا). لحل مشكلة توصيل الطلبات الخمسة، نحتاج نحدد الترتيب الأمثل للزيارة.

هذه المشكلة (VRP) صعبة جداً حسابياً. الحل الكامل مستحيل للعدد الكبير من النقاط. لذلك، نلجأ للحلول التقريبية (Heuristics). أبسطها هو خوارزمية “الجار الأقرب” (Nearest Neighbor):

  1. ابدأ من المستودع.
  2. من بين كل العملاء الذين لم تتم زيارتهم، اذهب إلى الأقرب.
  3. كرر الخطوة 2 حتى تزور كل العملاء.
  4. ارجع إلى المستودع.

هذا الحل مش دايماً بيعطي النتيجة المثالية، لكنه سريع جداً وبيعطي نتيجة “جيدة بما فيه الكفاية” في معظم الأحيان.

الخطوة 4: أهمية التخزين المؤقت (Caching)

حساب المسارات، خصوصاً في خرائط المدن الكبيرة، عملية مكلفة. لو عندك 100 سائق وكلهم بدهم يروحوا من المستودع للمنطقة (س)، ما في داعي نحسب المسار 100 مرة!

نصيحة عملية: قم بتخزين نتائج حسابات المسارات الشائعة في ذاكرة مؤقتة (Cache) مثل Redis. إذا تم طلب مسار تم حسابه مسبقاً، أرجعه مباشرة من الكاش. هذا يقلل الحمل على الخادم بشكل هائل ويجعل النظام يستجيب بسرعة البرق.

د) قياس الأداء: كيف نعرف أن حلنا ناجح؟

بعد ما تبني النظام، كيف بتتأكد إنه فعّال؟ لازم تقيس!

  • زمن الاستجابة (Query Time): قس متوسط الزمن اللازم لحساب مسار بين نقطتين. قارن بين أداء Dijkstra و A*. ستلاحظ أن A* أسرع بكثير كلما زادت المسافة بين النقطتين، لأن الـ Heuristic يوجه البحث بذكاء.
  • جودة الحل: هل المسار اللي بتعطيه هو الأفضل فعلاً؟ قارن نتائجك مع خدمة معروفة مثل Google Maps API لمجموعة من المسارات كعينة. إذا كانت الفروقات بسيطة (مثلاً 1-5%)، فهذا مؤشر ممتاز على أن نموذجك يعمل بشكل جيد.
  • استهلاك الذاكرة: تحميل خريطة مدينة كاملة في الذاكرة ممكن يستهلك جيجابايتات. راقب استهلاك الذاكرة، وفكر في حلول متقدمة للخرائط الضخمة مثل تقسيم الغراف (Graph Partitioning) أو تحميل أجزاء من الخريطة عند الحاجة فقط.

الخلاصة والنصيحة الأخيرة 💡

حل مشاكل المسارات اللوجستية هو مزيج رائع بين فهم المشكلة على أرض الواقع، والقدرة على تجريدها لنموذج رياضي، ثم تطبيق الخوارزميات الصحيحة لحلها. خوارزميات مثل Dijkstra و A* ليست مجرد نظريات أكاديمية، بل هي أدوات عملية وقوية تُستخدم يومياً في أنظمة الملاحة التي نعتمد عليها جميعاً.

نصيحتي لك كمبرمج أو مطور: لا تخف من هذه المواضيع. ابدأ صغيراً. حمل خريطة حيك باستخدام osmnx، حاول أن تجد أقصر مسار بين بيتك وأقرب بقالة، ثم توسع من هناك. التجربة والممارسة هما أفضل معلم.

تذكر دائماً قصة صديقي خالد، الذي تحول من صاحب مشروع على وشك الإفلاس إلى مدير لعملية لوجستية فعالة، كل هذا بفضل ترجمة مشكلة واقعية إلى لغة الخوارزميات. القوة الحقيقية للبرمجة تكمن في قدرتها على حل مشاكل حقيقية وملموسة. بالتوفيق يا كرام! 😊

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

أتمتة العمليات

قهوتك الصباحية مع ملخص الإنجازات: كيف تبني داشبورد يومي يصلك على الموبايل باستخدام n8n والذكاء الاصطناعي

كف عن تشتيت نفسك كل صباح بين Jira وGitHub والإيميلات. تعلم معي، أبو عمر، كيف تبني ورك فلو أتمتة يرسل لك ملخصاً ذكياً ومنسقاً بإنجازات...

12 فبراير، 2026 قراءة المزيد
البودكاست