كانت خرائطنا تضلل المستخدمين: كيف أنقذتنا خوارزمية A* من جحيم المسارات غير المنطقية؟

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

صارت توصلنا شكاوى غريبة من السائقين: “التطبيق بحكيلي ألف من شارع مسدود!”، “ليش وداني على طريق ترابي مع إني بقدر أروح من الأوتوستراد؟”، “المسار اللي أعطاني إياه التطبيق أطول بـ 15 دقيقة من الطريق اللي بعرفه!”. بصراحة، كان الوضع محرج ومُحبط. كنا في جحيم حقيقي من المسارات غير المنطقية، والمصداقية تبعتنا صارت على المحك. في ليلة من الليالي، وأنا قاعد بقلّب في الكود والخرائط اللي صارت تشبه معكرونة السباغيتي، صرخت على زميلي: “يا زلمة، لازم نلاقي حل! تطبيقنا صار أضحوكة!”. ومن هنا، بدأت رحلتنا مع المنقذ: خوارزمية A*.

المشكلة: لماذا كانت خوارزمياتنا “غبية”؟

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

تخيل أنك تبحث عن طريق بين نقطتين في مدينة. خوارزمية BFS ستعامل الطريق السريع المفتوح (خطوة واحدة طويلة) بنفس أهمية الدخول في زقاق ضيق ومزدحم (خطوة واحدة قصيرة). النتيجة؟ قد تختار لك مساراً يمر بعشرة أزقة ضيقة بدلاً من السير لـ 5 دقائق على طريق سريع، فقط لأن عدد “التقاطعات” أو “الخطوات” أقل. كانت خوارزميتنا عمياء، تبحث في كل الاتجاهات بنفس الأولوية، مثل شخص أعمى يتلمس طريقه في غرفة مزدحمة، مما يستهلك وقتاً وذاكرة هائلين ويعطي نتائج غير واقعية.

المنقذ: خوارزمية A* (نجمة ألفا) واللمسة السحرية

خوارزمية A* (تلفظ A-Star) هي خوارزمية بحث عن مسار ذكية للغاية. هي ليست عمياء مثل سابقاتها، بل لديها “بصيرة” أو “حدس” يمكنها من اتخاذ قرارات أفضل. هذا الحدس يأتي من معادلة بسيطة ولكنها عبقرية:

f(n) = g(n) + h(n)

قد تبدو المعادلة معقدة، لكنها في الحقيقة بسيطة جداً. خلينا نفصصها حبة حبة:

ما هي g(n): تكلفة المسار الفعلي

المتغير g(n) يمثل التكلفة الفعلية للوصول من نقطة البداية إلى النقطة الحالية (n). هذه هي المسافة التي قطعناها بالفعل. إنها الجزء المعروف والمؤكد من الرحلة. في تطبيق الخرائط، يمكن أن تكون هذه المسافة بالكيلومترات، أو الوقت المستغرق لقطعها مع الأخذ في الاعتبار السرعات المحددة.

ما هي h(n): تكلفة المسار المتوقع (الحدس)

هنا يكمن سحر A*! المتغير h(n) هو دالة الكلفة الحدسية (Heuristic Function). إنها “أفضل تخمين” أو تقدير للتكلفة من النقطة الحالية (n) إلى نقطة النهاية. هذا التخمين لا يجب أن يكون دقيقاً 100%، ولكنه يعطي الخوارزمية فكرة عن الاتجاه الذي يجب أن تسلكه.

على سبيل المثال، أبسط أشكال الحدس هو “المسافة الجوية المستقيمة” (as the crow flies) بين النقطة الحالية والهدف. من البديهي أنك لن تستطيع السفر عبر المباني، لكن هذا التقدير يوجه بحثك نحو الهدف بشكل عام، بدلاً من البحث في الاتجاه المعاكس تماماً.

ما هي f(n): التكلفة الإجمالية المقدرة

ببساطة، f(n) هي مجموع الجزأين: التكلفة الفعلية التي قطعناها (g) + التكلفة التقديرية المتبقية (h). خوارزمية A* في كل خطوة، تختار النقطة التي لديها أقل قيمة لـ f(n). بهذا، هي توازن بذكاء بين المسار الذي سلكته بالفعل والمسار الذي تعتقد أنه الأفضل للمستقبل.

كيف تفكر خوارزمية A*: مثال عملي

تخيل أنك في شبكة مربعات (Grid) وتريد الانتقال من المربع الأخضر (بداية) إلى المربع الأحمر (نهاية)، مع وجود جدار (أسود) لا يمكن عبوره.

A* grid example

تستخدم A* قائمتين رئيسيتين:

  • القائمة المفتوحة (Open List): تحتوي على المربعات التي لم نزرها بعد ولكننا نفكر في زيارتها. نرتبها حسب قيمة f(n) الأصغر.
  • القائمة المغلقة (Closed List): تحتوي على المربعات التي زرناها بالفعل وقمنا بتقييمها. لن نعود إليها مرة أخرى.

الخطوات بشكل مبسط:

  1. تبدأ من نقطة البداية، تضعها في القائمة المفتوحة.
  2. تختار المربع صاحب أقل قيمة f(n) من القائمة المفتوحة (في البداية سيكون هو نقطة البداية نفسها).
  3. تنقل هذا المربع من القائمة المفتوحة إلى القائمة المغلقة.
  4. تفحص كل جيرانه الصالحين (الذين يمكن المشي إليهم).
  5. لكل جار:
    • إذا كان هو نقطة النهاية، مبروك! لقد وجدت المسار.
    • وإلا، احسب له قيم g و h و f.
    • إذا كان هذا الجار ليس في القائمة المفتوحة، أضفه. إذا كان موجوداً بالفعل، تحقق إذا كان هذا المسار الجديد إليه أفضل (g أقل)، وقم بتحديثه.
  6. كرر من الخطوة 2 حتى تجد النهاية أو تصبح القائمة المفتوحة فارغة (مما يعني عدم وجود مسار).

هذا المزيج بين استكشاف المسارات الواعدة (بفضل h) وتتبع التكلفة الحقيقية (بفضل g) هو ما يجعل A* تتجنب بذكاء الطرق الطويلة غير المنطقية وتتجه مباشرة نحو الحل الأمثل.

من النظرية إلى الكود: مثال بايثون بسيط

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


# This is a very simplified example to illustrate the concept.
# In a real-world scenario, you'd use more robust data structures.

import heapq # To implement the priority queue for the Open List

class Node:
    def __init__(self, parent=None, position=None):
        self.parent = parent
        self.position = position
        self.g = 0
        self.h = 0
        self.f = 0

    def __eq__(self, other):
        return self.position == other.position

    # For the priority queue (heapq)
    def __lt__(self, other):
        return self.f  0:
        # Get the current node (the one with the lowest f value)
        current_node_tuple = heapq.heappop(open_list)
        current_node = current_node_tuple[1]
        
        # Add to closed list
        closed_list.add(current_node.position)

        # Found the goal
        if current_node == end_node:
            path = []
            current = current_node
            while current is not None:
                path.append(current.position)
                current = current.parent
            return path[::-1] # Return reversed path

        # Generate children (neighbors)
        for new_position in [(0, -1), (0, 1), (-1, 0), (1, 0)]: # Neighbors (up, down, left, right)
            
            node_position = (current_node.position[0] + new_position[0], current_node.position[1] + new_position[1])

            # Make sure within range and walkable
            if node_position[0] > (len(grid) - 1) or node_position[0]  (len(grid[0]) - 1) or node_position[1] = open_node.g:
                    break
            else:
                heapq.heappush(open_list, (child.f, child))

    return None # Return None if no path is found

# Example Usage
grid = [[0, 0, 0, 0, 1, 0],
        [0, 1, 0, 0, 0, 0],
        [0, 0, 1, 0, 1, 0],
        [0, 0, 0, 0, 1, 0],
        [0, 0, 0, 0, 0, 0]] # 1 is a wall

start = (0, 0)
end = (4, 5)

path = astar(grid, start, end)
print(path) # Output: [(0, 0), (0, 1), (0, 2), (1, 2), (2, 3), (3, 3), (4, 3), (4, 4), (4, 5)]

نصائح أبو عمر العملية

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

  • جودة الحدس (Heuristic) هي كل إشي: دالة الحدس هي قلب A* النابض. إذا كانت دالة الحدس تبعتك سيئة (مثلاً، تعطي تقديرات مبالغ فيها جداً)، ممكن A* ما تلاقي لك أقصر طريق. القاعدة الذهبية: يجب أن يكون الحدس “مقبولاً” (Admissible)، أي أنه لا يبالغ في تقدير التكلفة الحقيقية أبداً. الحدس المثالي هو الذي يقترب من التكلفة الحقيقية قدر الإمكان دون أن يتجاوزها.
  • ليست دائماً الأسرع: على الخرائط الصغيرة، قد لا تلاحظ فرقاً كبيراً في الأداء. لكن على الخرائط الضخمة جداً (مثل خريطة بلد كامل)، يمكن أن تستهلك A* ذاكرة كبيرة جداً بسبب القائمة المفتوحة. هناك نسخ محسنة من A* مثل IDA* أو Jump Point Search تتعامل مع هذه المشاكل.
  • العالم الحقيقي متغير: في تطبيقات التوصيل، الخريطة ليست ثابتة. هناك زحمة سير، حوادث، طرق مغلقة. A* الكلاسيكية تجد مساراً بناءً على صورة ثابتة. في الواقع، تحتاج إلى تحديث “تكلفة” المسارات باستمرار (مثلاً، شارع مزدحم تصبح تكلفة المرور فيه أعلى) وإعادة حساب المسار عند اللزوم.

الخلاصة: من جحيم الفوضى إلى جمال المنطق

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

نصيحتي لكل مبرمج ومطور: لا تحفظوا الخوارزميات، بل افهموا فلسفتها. عندما تفهم “لماذا” تعمل A* بهذا الشكل، ستتمكن من تكييفها وتطويرها لحل مشاكل لم تكن لتتخيلها. بالتوفيق يا جماعة! 😊

أبو عمر

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

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

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

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

آخر المدونات

الحوسبة السحابية

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

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

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

مقابلاتي التقنية كانت كارثة: كيف أنقذني ‘التفكير بصوت عالٍ’ من جحيم الفشل؟

أشارككم قصة شخصية عن فشلي في المقابلات التقنية بسبب الصمت القاتل، وكيف غيرت استراتيجية "التفكير بصوت عالٍ" مساري المهني. اكتشفوا معي كيف تحولون المقابلة من...

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

كان مستخدمونا في الطرف الآخر من العالم ينتظرون إلى الأبد: كيف أنقذتنا شبكات توصيل المحتوى (CDN) من جحيم زمن الاستجابة المرتفع؟

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

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

من شبكة مثقوبة إلى حصن منيع: كيف أنقذتنا قواعد البيانات الرسومية من كابوس الاحتيال؟

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

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

ميزانيات الخطأ (Error Budgets): كيف أنهت كابوس مكالمات منتصف الليل وأنقذتنا من الإرهاق؟

كنا غارقين في مكالمات طوارئ ليلية لا تنتهي، فريق منهك والمنتج على المحك. في هذه المقالة، أشارككم قصة كيف أنقذنا مفهوم "ميزانيات الخطأ" (Error Budgets)...

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

كانت اجتماعاتنا الفردية استجواباً صامتاً: كيف حولنا الـ 1-on-1 من تقرير حالة ممل إلى محرك لنمو الفريق؟

أشارككم تجربتي كقائد فريق تقني في تحويل الاجتماعات الفردية (1-on-1s) من جلسات استجواب مملة إلى محادثات مثمرة تساهم في بناء الثقة وتطوير الفريق. هذه المقالة...

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

كانت اختباراتنا تصرخ ‘الذئب’: كيف قضينا على ‘الاختبارات المتقلبة’ (Flaky Tests) واستعدنا الثقة في خطوط الأنابيب؟

في هذه المقالة، أشارككم قصة من أرض المعركة البرمجية، وكيف تغلب فريقي على كابوس "الاختبارات المتقلبة" أو Flaky Tests. سنغوص في أسبابها الخفية، ونتعلم استراتيجيات...

30 مايو، 2026 قراءة المزيد
أدوات وانتاجية

كانت أصابعي تصرخ من التكرار: كيف أنقذتني ‘مقتطفات الشفرة’ (Code Snippets) من جحيم كتابة Boilerplate؟

أشارككم قصتي مع التكرار الممل في البرمجة وكيف غيرت "مقتطفات الشفرة" (Code Snippets) طريقة عملي تماماً. دليل عملي من مبرمج فلسطيني لزيادة إنتاجيتك والتخلص من...

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

كانت تبعياتنا قنبلة موقوتة: كيف أنقذنا ‘التحديث الآلي للتبعيات’ من جحيم الثغرات الأمنية المنسية؟

أشارككم قصة حقيقية عن ليلة كادت فيها ثغرة أمنية في إحدى المكتبات المنسية أن تدمر مشروعنا بالكامل. اكتشفوا معنا كيف تحولنا من الفوضى إلى الأمان...

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