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

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

أبو عمر

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

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

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

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

آخر المدونات

تجربة المستخدم والابداع البصري

كانت واجهاتنا جزرًا معزولة: كيف أنقذنا ‘نظام التصميم’ من جحيم الفوضى البصرية؟

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

17 مايو، 2026 قراءة المزيد
برمجة وقواعد بيانات

كانت تحديثات قاعدة البيانات كابوساً: كيف أنقذتنا أدوات الترحيل (Migrations) من جحيم التعديلات اليدوية؟

هل عانيت يوماً من تحديث مخطط قاعدة البيانات يدوياً بين فريقك؟ أبو عمر يشارككم قصة حقيقية حول كيف غيّرت أدوات الترحيل (Migrations) طريقة عمل فريقه،...

17 مايو، 2026 قراءة المزيد
الشبكات والـ APIs

كانت خوادمنا تستجدي التحديثات: كيف أنقذتنا ‘خطاطيف الويب’ (Webhooks) من جحيم الاستقصاء المستمر (Polling)؟

تخيل خوادمك تلهث من كثرة الطلبات غير الضرورية. في هذه المقالة، أشارككم قصة حقيقية من الميدان حول كيفية انتقالنا من جحيم الاستقصاء المستمر (Polling) إلى...

17 مايو، 2026 قراءة المزيد
الحوسبة السحابية

كانت بنيتنا التحتية قصراً من رمال: كيف أنقذتنا “البنية التحتية ككود” (IaC) من جحيم البيئات المتضاربة؟

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

17 مايو، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

كان ملفي الشخصي مقبرة لمشاريع الدورات: كيف أنقذتني ‘المساهمة في المصادر المفتوحة’ من جحيم الرفض التلقائي؟

هل تشعر أن ملفك الشخصي على GitHub لا يعكس قدراتك الحقيقية؟ في هذه المقالة، أشاركك يا صديقي تجربتي الشخصية، أنا أبو عمر، وكيف انتقلت من...

17 مايو، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

كانت قاعدة بياناتنا تتوسل الرحمة: كيف أنقذنا التخزين المؤقت (Caching) من جحيم الاستعلامات البطيئة

قصة حقيقية من واقع العمل عن كيفية انهيار نظامنا تحت ضغط الاستعلامات المتكررة، وكيف كان التخزين المؤقت (Caching) هو طوق النجاة. مقالة عملية للمطورين تشرح...

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

كان التحقق من هوية عملائنا يستغرق أياماً: كيف أنقذنا الذكاء الاصطناعي (eKYC) من جحيم الإجراءات اليدوية؟

بصفتي مبرمجاً فلسطينياً، سأروي لكم حكايتنا مع كابوس التحقق اليدوي من هوية العملاء (KYC) وكيف كانت رحلة الانتقال إلى التحقق الإلكتروني (eKYC) باستخدام الذكاء الاصطناعي...

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

كانت أعطالنا تكتشف بعد فوات الأوان: كيف أنقذنا Prometheus من جحيم المراقبة التفاعلية؟

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

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