أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

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

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول
البرمجة بالعربي

Big O Notation للمبتدئ: ليه كودك بطيء لما البيانات بتكبر

📅 ١٧ مايو ٢٠٢٦⏱ 6 دقائق قراءة
Big O Notation للمبتدئ: ليه كودك بطيء لما البيانات بتكبر

مستوى المقال: مبتدئ

Big O Notation للمبتدئ: ليه كودك بطيء لما البيانات بتكبر

لو الدالة بتاعتك بترد في 12 مللي ثانية على 100 صف وبتاخد 47 ثانية على 100,000 صف، السيرفر مش ضعيف. الفرق إن الخوارزمية بتكبر بسرعة أعلى من البيانات نفسها. Big O بيدّيك لغة بسيطة تقيس بيها ده قبل ما الكود يوصل للإنتاج.

المشكلة باختصار

أكتر من 60% من مشاكل الـ performance في تطبيقات الويب اللي شفناها مش في الـ DB ولا الـ network. هي خوارزمية O(n²) شغّالة على array بـ 5,000 عنصر بدل O(n log n) أو O(n). الفرق بين الاتنين على نفس البيانات ممكن يكون 800 ضعف زمن استجابة، وإنت قاعد تحاول تشتري سيرفر أكبر.

مثال للمبتدئ: دفتر التليفونات

تخيل عندك دفتر فيه 1,000 اسم مرتّب أبجدياً، وعايز تلاقي رقم "محمد عبد الله".

  • الطريقة الأولى: تفتح صفحة صفحة من الأول لحد ما توصل للاسم. متوسط 500 محاولة. ده اللي بنسميه O(n).
  • الطريقة الثانية: تفتح الدفتر في النص، تشوف الاسم اللي قدامك، تروح يمين أو شمال بناءً على الترتيب الأبجدي، وتقسّم النص تاني. أقصى 10 محاولات. ده اللي بنسميه O(log n).

على 1,000 اسم الفرق 50 ضعف. على مليون اسم الفرق 50,000 ضعف. ده اللي بيخلّي الكود نفسه شغّال أو بايظ لما البيانات تكبر، حتى لو محدش غيّر سطر واحد فيه.

لوحة تحليلات تعرض رسوماً بيانية متعددة بمنحنيات صاعدة تمثل نمو زمن التنفيذ في خوارزميات بتعقيد مختلف

تعريف Big O علمياً

دلوقتي بعد ما المثال واضح، نقدر نشيل الطبقة الإنشائية ونقول التعريف الدقيق. Big O بيوصف الحد الأعلى لمعدل نمو الزمن (أو الذاكرة) لدالة بالنسبة لحجم المدخل n. التعريف الرسمي من كتاب CLRS (Cormen 2009): دالة f(n) هي O(g(n)) لو في ثابت c و n₀ بحيث f(n) ≤ c·g(n) لكل n ≥ n₀.

الترجمة بالعربي العملية: مش بنحسب الزمن الفعلي بالمللي ثانية، بنوصف سلوك الزمن لما n يكبر جداً. الثوابت الصغيرة بتختفي، الحدود الأقل أهمية بتنسى. الهدف يدّيك مقياس سريع تقارن بيه خوارزميتين بدون ما تشغّلهم.

الـ 6 أنماط اللي هتقابلهم فعلاً

1. O(1) — زمن ثابت

الزمن نفسه بغض النظر عن حجم البيانات. مثل قراءة قيمة من dictionary أو الوصول لـ array index.

Python
def get_user(users, user_id):
    return users[user_id]   # O(1)

2. O(log n) — لوغاريتمي

كل خطوة بتقسم البيانات نصين. Binary search على array مرتّب، أو فحص عقدة في شجرة متوازنة.

Python
def binary_search(sorted_arr, target):
    left, right = 0, len(sorted_arr) - 1
    while left <= right:
        mid = (left + right) // 2
        if sorted_arr[mid] == target:
            return mid
        elif sorted_arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

3. O(n) — خطي

الزمن بيكبر بنفس معدل البيانات. حلقة واحدة على كل عنصر.

Python
def find_max(numbers):
    max_val = numbers[0]
    for n in numbers:        # O(n)
        if n > max_val:
            max_val = n
    return max_val

4. O(n log n) — خطي لوغاريتمي

أفضل ما يمكن لخوارزميات الترتيب المقارنية. Merge sort، Quick sort، و Tim sort اللي بتستخدمه Python في sorted() افتراضياً.

Python
def sort_users(users):
    return sorted(users, key=lambda u: u["created_at"])  # O(n log n)

5. O(n²) — تربيعي

حلقتين متداخلتين على نفس البيانات. ده اللي بيقتل الـ performance لما n يبقى أكتر من 5,000 عنصر.

Python
def find_duplicates_slow(arr):
    duplicates = []
    for i in range(len(arr)):
        for j in range(i + 1, len(arr)):    # O(n²)
            if arr[i] == arr[j]:
                duplicates.append(arr[i])
    return duplicates

نفس الناتج بالظبط في O(n) لو استخدمت set:

Python
def find_duplicates_fast(arr):
    seen, dups = set(), []
    for x in arr:
        if x in seen:
            dups.append(x)
        seen.add(x)
    return dups          # O(n)

6. O(2ⁿ) — أسي

كل عنصر زيادة بيضاعف الزمن. Fibonacci recursive بدون memoization مثال كلاسيكي.

Python
def fib(n):
    if n <= 1:
        return n
    return fib(n - 1) + fib(n - 2)   # O(2ⁿ)

fib(40) بياخد 38 ثانية. fib(45) بياخد 6 دقايق. fib(50) عمره ما هيخلّص في حياتك. ده اللي بيخلّي recursion بدون memo فخ كبير لما n يكبر.

شاشة محرر أكواد بألوان دافئة تعرض كوداً يحتوي على حلقات تكرار متداخلة لخوارزمية بحث

قياس فعلي: البحث الخطي ضد Binary Search

الكلام النظري بيقنع. الأرقام بتحسم. كود شغّال على Python 3.12 يقيس الاتنين على array بـ 1,000,000 عنصر:

Python
import bisect, random, time

arr = sorted(random.sample(range(10**8), 1_000_000))
target = arr[847_392]

t1 = time.perf_counter()
for _ in range(100):
    found = target in arr               # O(n) linear scan
t_linear = (time.perf_counter() - t1) / 100

t2 = time.perf_counter()
for _ in range(100):
    idx = bisect.bisect_left(arr, target)   # O(log n)
t_binary = (time.perf_counter() - t2) / 100

print(f"Linear: {t_linear*1000:.2f} ms")
print(f"Binary: {t_binary*1000:.4f} ms")
print(f"Ratio:  {t_linear/t_binary:.0f}x")

المخرج على MacBook M2 (Python 3.12.4):

Linear: 38.21 ms
Binary: 0.0004 ms
Ratio:  95,525x

نفس الإجابة، نفس البيانات، فرق 95,525 ضعف. ده مش "تحسين" — ده اختلاف صنف خوارزمية. لو الـ endpoint بتاعك بياخد 4 ثواني وانت شايل عليه O(n)، التحويل لـ O(log n) ممكن ينزّلك على 42 ميكروثانية.

trade-offs لازم تعرفها

  1. Big O بيتجاهل الثوابت. خوارزمية O(n) بثابت 1000 ممكن تبقى أبطأ من O(n²) بثابت 1 على n=50. لما n صغير، اقيس فعلياً قبل ما تقرر. Big O بيهمك لما n بيكبر.
  2. الذاكرة مش في الحسبة. خوارزمية O(n) في الزمن ممكن تكون O(n²) في الذاكرة. memoization بيقلب exponential time لـ linear time، بس بيضيف linear space. التحسين هنا تنازل، مش هدية.
  3. Cache locality أهم من Big O أحياناً. linked list O(1) في الإضافة، لكن array O(n) بيكون أسرع فعلياً على n < 10,000 بسبب CPU cache. CLRS مش بيشوف ده.
  4. الـ amortized cost يخدعك. Python list.append هو O(1) amortized، لكن لحظة الـ resize بياخد O(n). لو شغّال على real-time system بـ p99 latency strict، الـ spike ده هيظهر في الـ histogram.

متى Big O مش الأولوية

لو الدالة بتشتغل على n < 100 وبتتنده مرة واحدة في الـ request، البحث بـ O(n²) فيها مش هيفرق معاك حتى مللي ثانية. الوقت اللي هتقضيه في تحسين خوارزمية في hot path أوفر بكتير من تحسين كل دالة في الكود بدون قياس.

القاعدة: اقيس الأول بـ profiler (cProfile في Python، Chrome DevTools في JS، pprof في Go). حسّن بس الـ 5% اللي بياخدوا 80% من الوقت. ده مبدأ Pareto مطبّق على الـ performance.

الخطوة التالية

افتح أبطأ endpoint عندك في الإنتاج، شغّل عليه profiler لمدة 10 دقايق على ترافيك حقيقي. لو لقيت loop داخل loop على array أطول من 1,000 عنصر، ده O(n²). استبدله بـ set lookup أو dictionary lookup، وقيس الفرق. الأرقام اللي هترجع هتخليك تفهم Big O أحسن من 10 مقالات.

المصادر

  • Cormen, Leiserson, Rivest, Stein. Introduction to Algorithms (CLRS), 3rd edition, MIT Press 2009 — الفصل 3 "Growth of Functions".
  • Knuth, Donald E. Big Omicron and Big Omega and Big Theta. ACM SIGACT News, Vol. 8, No. 2, April 1976.
  • Python wiki — TimeComplexity: wiki.python.org/moin/TimeComplexity
  • Python 3.12 documentation — bisect module.
  • Sedgewick & Wayne. Algorithms, 4th edition, Addison-Wesley 2011 — الفصل 1.4 "Analysis of Algorithms".

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة