طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 101 · تركيب البيانات والخوارزميات
الدرس 25 · الوحدة 12: حلول تطبيقية لمشكلات واقعية

دراسة حالة: نظام توصيل

كل ما تعلمته في نظام واحد: من شكاوى المدير إلى حل مقيس. ⏱ نحو 45 دقيقة.

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

١المشكلة

متجر في المدينة المنورة يوصّل الطلبات من مستودع واحد إلى الأحياء. مدير العمليات يشتكي:

هذه أربع مشكلات، ولكل منها تركيب أو خوارزمية مما تعلمته في المقرر.

٢التحليل والقرارات

المتطلبالعملية المتكررةالقرارالتعقيد
البحث عن طلب برقمهبحث بمفتاح آلاف المراتقاموس: رقم الطلب ← البياناتO(1)
المستعجل أولًا، ثم بترتيب الوصولإضافة وإخراج الأهم باستمراركومة: (الأولوية، رقم الوصول، الطلب)O(log n)
أقصر طريق من المستودعمسافات موزونة موجبةدايكستراO((V + E) log V)
أكثر الأحياء طلبًاعدّCounterO(n)

٣التنفيذ

main.py
import heapqfrom collections import deque # ---------- البيانات ----------roads = [("المستودع", "العزيزية", 6), ("المستودع", "قباء", 4), ("قباء", "العوالي", 3),         ("العزيزية", "العوالي", 5), ("العزيزية", "الحرة", 7), ("العوالي", "الحرة", 2),         ("الحرة", "سلطانة", 4), ("قباء", "سلطانة", 9)]graph = {}for a, b, km in roads:    graph.setdefault(a, []).append((b, km))    graph.setdefault(b, []).append((a, km)) orders = {}                      # رقم الطلب ← بياناته: بحث O(1)queue = []                       # كومة: (الأولوية، رقم الوصول، رقم الطلب)arrival = 0 def add_order(oid, customer, district, express=False):    global arrival    orders[oid] = {"customer": customer, "district": district, "status": "جديد"}    heapq.heappush(queue, (0 if express else 1, arrival, oid))    arrival += 1 def shortest(start):             # دايكسترا    dist, heap = {start: 0}, [(0, start)]    while heap:        d, u = heapq.heappop(heap)        if d > dist[u]:            continue        for v, w in graph[u]:            if d + w < dist.get(v, float("inf")):                dist[v] = d + w                heapq.heappush(heap, (d + w, v))    return dist # ---------- يوم عمل ----------add_order(101, "سعود", "العوالي")add_order(102, "نورة", "سلطانة", express=True)add_order(103, "خالد", "الحرة")add_order(104, "ريم", "العوالي", express=True) print("بحث عن 103:", orders[103]) dist = shortest("المستودع")print("المسافات:", dict(sorted(dist.items(), key=lambda x: x[1]))) print("ترتيب التوصيل:")while queue:    pr, _, oid = heapq.heappop(queue)    o = orders[oid]    o["status"] = "مُسلَّم"    kind = "مستعجل" if pr == 0 else "عادي"    print(f"  {oid} | {kind} | {o['customer']} | {o['district']} | {dist[o['district']]} كم")
Shell×
>>> %Run main.py
بحث عن 103: {'customer': 'خالد', 'district': 'الحرة', 'status': 'جديد'}
المسافات: {'المستودع': 0, 'قباء': 4, 'العزيزية': 6, 'العوالي': 7, 'الحرة': 9, 'سلطانة': 13}
ترتيب التوصيل:
102 | مستعجل | نورة | سلطانة | 13 كم
104 | مستعجل | ريم | العوالي | 7 كم
101 | عادي | سعود | العوالي | 7 كم
103 | عادي | خالد | الحرة | 9 كم
>>>

٤قِس قبل أن تقول «أسرع»

نقارن البحث عن طلب في قائمة بالبحث في القاموس، مع 20 ألف طلب، ونبحث عن آخر 100 طلب:

main.py
import time n = 20_000order_list = [(i, "عميل") for i in range(n)]order_dict = {i: "عميل" for i in range(n)}targets = list(range(n - 100, n))           # الحالة الأسوأ: في آخر القائمة t = time.perf_counter()for x in targets:    for oid, c in order_list:        if oid == x:            breakt_list = time.perf_counter() - t t = time.perf_counter()for x in targets:    c = order_dict[x]t_dict = time.perf_counter() - t print("القائمة:", round(t_list * 1000), "ms | القاموس:", round(t_dict * 1000, 3), "ms")
Shell×
>>> %Run main.py
القائمة: 103 ms | القاموس: 0.011 ms
>>>

٥تقرير الحالة

هكذا يُكتب تقرير دراسة الحالة كما تطلب الخطة: شرح الحل، وتحليل أدائه، وتوصيات:

البندالمحتوى
المشكلةأربع شكاوى: بحث بطيء، وأولوية مفقودة، وطرق طويلة، وتقارير غائبة.
الحلقاموس للطلبات، وكومة للأولوية، ودايكسترا للطرق، و Counter للتقارير.
التحليلالبحث من O(n) إلى O(1)، والإخراج بالأولوية O(log n)، والطرق O((V + E) log V).
القياسالبحث في القاموس أسرع بآلاف المرات على 20 ألف طلب (القياس في القسم 4).
التوصياتمع مئات السائقين: توزيع الطلبات على أقرب سائق (مشكلة أصعب قريبة من البائع المتجول؛ تُحل تقريبيًّا).

توقّع وتحقّق

البحث عن طلب برقمه آلاف المرات:

O(1).

المستعجل أولًا ثم بترتيب الوصول:

heapq.

أقصر طريق بالكيلومترات:

أوزان موجبة.

أكثر الأحياء طلبًا:

عدّ.

الخلاصة

تحدٍّ برمجي: تقرير الأحياء والمسافات

متقدم

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

المطلوب:
  • أضف الطلبات الخمسة المعطاة.
  • Counter لأحياء الطلبات: اطبع كل حي وعدده من الأكثر.
  • المسافة الكلية = مجموع 2 × المسافة لكل طلب: المجموع: 80 كم

ابدأ من هذا الكود، واضغط ▶ شغّل هنا لتكتب حلّك وتجرّبه مباشرة:

main.py
import heapqfrom collections import deque # ---------- البيانات ----------roads = [("المستودع", "العزيزية", 6), ("المستودع", "قباء", 4), ("قباء", "العوالي", 3),         ("العزيزية", "العوالي", 5), ("العزيزية", "الحرة", 7), ("العوالي", "الحرة", 2),         ("الحرة", "سلطانة", 4), ("قباء", "سلطانة", 9)]graph = {}for a, b, km in roads:    graph.setdefault(a, []).append((b, km))    graph.setdefault(b, []).append((a, km)) orders = {}                      # رقم الطلب ← بياناته: بحث O(1)queue = []                       # كومة: (الأولوية، رقم الوصول، رقم الطلب)arrival = 0 def add_order(oid, customer, district, express=False):    global arrival    orders[oid] = {"customer": customer, "district": district, "status": "جديد"}    heapq.heappush(queue, (0 if express else 1, arrival, oid))    arrival += 1 def shortest(start):             # دايكسترا    dist, heap = {start: 0}, [(0, start)]    while heap:        d, u = heapq.heappop(heap)        if d > dist[u]:            continue        for v, w in graph[u]:            if d + w < dist.get(v, float("inf")):                dist[v] = d + w                heapq.heappush(heap, (d + w, v))    return dist from collections import Counter for oid, name, d in [(1, "أ", "العوالي"), (2, "ب", "الحرة"), (3, "ج", "العوالي"), (4, "د", "سلطانة"), (5, "هـ", "قباء")]:    add_order(oid, name, d)
المطلوب×
العوالي 2
الحرة 1
سلطانة 1
قباء 1
المجموع: 80 كم
💡 تلميح 1
Counter(o["district"] for o in orders.values())
💡 تلميح 2
المسافات: العوالي 7، الحرة 9، سلطانة 13، قباء 4.

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

main.py
import heapqfrom collections import deque # ---------- البيانات ----------roads = [("المستودع", "العزيزية", 6), ("المستودع", "قباء", 4), ("قباء", "العوالي", 3),         ("العزيزية", "العوالي", 5), ("العزيزية", "الحرة", 7), ("العوالي", "الحرة", 2),         ("الحرة", "سلطانة", 4), ("قباء", "سلطانة", 9)]graph = {}for a, b, km in roads:    graph.setdefault(a, []).append((b, km))    graph.setdefault(b, []).append((a, km)) orders = {}                      # رقم الطلب ← بياناته: بحث O(1)queue = []                       # كومة: (الأولوية، رقم الوصول، رقم الطلب)arrival = 0 def add_order(oid, customer, district, express=False):    global arrival    orders[oid] = {"customer": customer, "district": district, "status": "جديد"}    heapq.heappush(queue, (0 if express else 1, arrival, oid))    arrival += 1 def shortest(start):             # دايكسترا    dist, heap = {start: 0}, [(0, start)]    while heap:        d, u = heapq.heappop(heap)        if d > dist[u]:            continue        for v, w in graph[u]:            if d + w < dist.get(v, float("inf")):                dist[v] = d + w                heapq.heappush(heap, (d + w, v))    return dist from collections import Counter for oid, name, d in [(1, "أ", "العوالي"), (2, "ب", "الحرة"), (3, "ج", "العوالي"), (4, "د", "سلطانة"), (5, "هـ", "قباء")]:    add_order(oid, name, d) for district, n in Counter(o["district"] for o in orders.values()).most_common():    print(district, n) dist = shortest("المستودع")total = sum(2 * dist[o["district"]] for o in orders.values())print("المجموع:", total, "كم")
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس