WEEK 4 • تقنيات متقدمة

Greedy Algorithms

الخوارزميات الجشعة

إيه هي Greedy Algorithms؟

🤑 الفكرة الأساسية

📖 تشبيه أكل الحلويات

تخيل طفل قدامه حلويات كتير:

  • • بياخد أكبر حتة في كل مرة
  • • مش بيفكر في المستقبل
  • • كل خطوة = أفضل اختيار متاح دلوقتي

"Take the best option NOW, don't look back!"

🎯 الاستراتيجية

1️⃣ في كل خطوة، اختار الأفضل محلياً (Locally Optimal)
2️⃣ ما ترجعش في قراراتك (No Backtracking)
3️⃣ تأمل إن ده يوديك للحل الأمثل (Globally Optimal)

⚠️ تحذير مهم!

Greedy مش دايماً بيدي الحل الأمثل!
بس لما بينفع، بيكون سريع جداً وبسيط.

مثال: Coin Change

أقل عدد عملات لتكوين مبلغ

لما Greedy بينفع ✓

عملات: [25, 10, 5, 1] | المبلغ: 41

25
← أكبر عملة ≤ 41 41-25=16
10
← أكبر عملة ≤ 16 16-10=6
5
← أكبر عملة ≤ 6 6-5=1
1
← أكبر عملة ≤ 1 1-1=0 ✓

الإجابة: 4 عملات (أمثل!)

لما Greedy بيفشل ✗

عملات: [1, 3, 4] | المبلغ: 6

Greedy يقول:

4 + 1 + 1 = 3 عملات

الحل الأمثل:

3 + 3 = 2 عملات!

⚠️ Greedy ما لقاش الحل الأمثل!

مثال: Activity Selection

أكتر عدد أنشطة ممكن تعملها (من غير تعارض)

📋 المشكلة:

عندك أنشطة بأوقات بداية ونهاية. اختار أكتر عدد من غير تعارض.

الاستراتيجية الجشعة: دايماً اختار النشاط اللي ينتهي أولاً!

024681012
A (0-3) ✓
B (1-5) ✗
C (4-7) ✓
D (5-9) ✗
E (8-11) ✓

✓ الإجابة: A, C, E (3 أنشطة)

اخترنا اللي ينتهي أولاً في كل مرة، عشان نفضي وقت لأنشطة تانية!

Greedy vs DP

المقارنة Greedy Dynamic Programming
الاستراتيجية أفضل اختيار محلي جرب كل الاحتمالات
الحل الأمثل؟ أحياناً دايماً ✓
السرعة سريع جداً ✓ أبطأ
الذاكرة قليلة ✓ أكتر (Table)
التعقيد بسيط أصعب

مشاكل Greedy الشهيرة

Minimum Spanning Tree

أقل تكلفة لربط كل الـ nodes

Prim's, Kruskal's

Dijkstra's Algorithm

أقصر طريق من نقطة لباقي النقط

Huffman Coding

ضغط البيانات (Compression)

Activity Selection

أكتر عدد أنشطة من غير تعارض

Fractional Knapsack

Knapsack لما ممكن تقسم الحاجات

Job Scheduling

ترتيب المهام بأقل تأخير

Implementation

activitySelection.js
function activitySelection(activities) {
  // Sort by end time (Greedy choice)
  activities.sort((a, b) => a.end - b.end);
  
  const selected = [activities[0]];
  let lastEnd = activities[0].end;
  
  for (let i = 1; i < activities.length; i++) {
    // If no overlap, select it
    if (activities[i].start >= lastEnd) {
      selected.push(activities[i]);
      lastEnd = activities[i].end;
    }
  }
  
  return selected;
}

شرح الكود 📖

سطر 3: رتب حسب وقت النهاية (الجشعة!)
سطر 5: اختار أول نشاط (ينتهي أولاً)
سطر 10-12: لو ما فيش تعارض، اختاره