من شوارع فلسطين إلى عالم الخوارزميات: دليلك العملي لحل مشكلة مسارات التوصيل مع 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، حاول أن تجد أقصر مسار بين بيتك وأقرب بقالة، ثم توسع من هناك. التجربة والممارسة هما أفضل معلم.

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

أبو عمر

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

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

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

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

آخر المدونات

التوسع والأداء العالي والأحمال

عملياتي الطويلة كانت تجمد واجهة المستخدم: كيف أنقذتني ‘قوائم انتظار الرسائل’ (Message Queues) من جحيم تجربة المستخدم السيئة؟

واجهة المستخدم تتجمد والعملاء يشتكون؟ في هذه المقالة، أشارككم قصتي كـ 'أبو عمر' وكيف حولتني قوائم انتظار الرسائل من مطور محبط إلى باني أنظمة قوية...

2 أبريل، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

حساباتي البنكية كانت جزرًا معزولة: كيف أنقذتني ‘الصيرفة المفتوحة’ (Open Banking) من جحيم تجميع البيانات المالية يدويًا؟

أنا أبو عمر، مطور برمجيات فلسطيني، وأروي لكم كيف حوّلت "الصيرفة المفتوحة" فوضى حساباتي البنكية المتعددة إلى لوحة تحكم مالية ذكية. اكتشفوا معي هذه التقنية...

2 أبريل، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

تطبيقي كان يعمل على جهازي فقط: كيف أنقذتني ‘الحاويات’ (Containers) من جحيم ‘تعارض البيئات’؟

أشارككم قصة حقيقية عن كابوس "عندي شغال!" وكيف أصبحت تقنيات الحاويات مثل Docker أداتي السحرية لإنهاء صراعات البيئات المختلفة. هذه المقالة دليل عملي لكل مبرمج...

2 أبريل، 2026 قراءة المزيد
ادارة الفرق والتنمية البشرية

فريقي كان يخشى ارتكاب الأخطاء: كيف أنقذني بناء ‘الأمان النفسي’ من جحيم الإبداع المكبوت؟

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

2 أبريل، 2026 قراءة المزيد
اختبارات الاداء والجودة

تحديثاتي كانت تحطم الميزات القديمة: كيف أنقذتني ‘الاختبارات التراجعية الآلية’ من جحيم الخوف عند كل إصدار؟

أشارككم قصتي مع الخوف من تحديث البرمجيات وكيف كانت التحديثات الجديدة تكسر الميزات القديمة دون علمي. اكتشفوا معي كيف أصبحت "الاختبارات التراجعية الآلية" (Automated Regression...

2 أبريل، 2026 قراءة المزيد
​معمارية البرمجيات

تطبيقي المتجانس كان وحشاً لا يمكن ترويضه: كيف أنقذني ‘نمط الخانق’ (Strangler Fig Pattern) من جحيم إعادة الكتابة الكبرى؟

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

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