Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

Counting Sort

آخر تحديث

فرز العد هو فرز بلا مقارنات للأعداد الصحيحة ضمن نطاق معروف ومحدود. يحسب عدد مرات ظهور كل قيمة، ثم يستخدم تلك الأعداد لكتابة كل قيمة مباشرة في موضعها المرتّب - دون أي مقارنات. اضغط تشغيل بالأعلى لتشاهد القيم وهي تُحصى ثم تُوضع بالترتيب.

يعمل فرز العد بزمن O(n + k)، حيث k هو نطاق قيم الإدخال. عندما لا يكون k أكبر بكثير من n يكون سريعًا للغاية وقد يتفوق على خوارزميات الفرز بالمقارنة ذات O(n log n)، لكن إذا كان نطاق القيم ضخمًا فإن مصفوفة العد ذات O(k) تجعله غير عملي.

التعقيد الزمني والمكاني

الحالةالتعقيدملاحظات
الزمنO(n + k)n عنصرًا، k = نطاق القيم
المساحةO(n + k)مصفوفة العد + مصفوفة الإخراج
مستقرنعمعند الوضع من اليمين إلى اليسار باستخدام مجاميع البادئة
مقارنة؟لايفرز بالعد لا بالمقارنة
الأفضل لـنطاق أعداد صحيحة صغيرk قريب من n

خطوة بخطوة

الخطوةما الذي يحدث
1إيجاد القيمة القصوى لتحديد حجم مصفوفة العد.
2حساب عدد مرات ظهور كل قيمة.
3(اختياري) تحويل الأعداد إلى مجاميع بادئة لتحقيق الاستقرار.
4كتابة كل قيمة في الإخراج بعدد مرات ظهورها.
5أصبحت مصفوفة الإخراج مرتّبة بالكامل الآن.

مثال محلول

فرز [1, 4, 1, 2, 4] (القيم تتراوح من 0 إلى 4، لذا تحتوي مصفوفة العد على 5 خانات):

المرورالحالةالإجراء
مسح الإدخالcount = [0, 2, 1, 0, 2]حساب مرات الظهور: 1 يظهر مرتين، و2 مرة واحدة، و4 مرتين.
مجاميع البادئةcount = [0, 2, 3, 3, 5]تحمل كل خانة الآن عدد القيم التي هي <= لدليلها، مما يعطي المواضع النهائية.
وضع 4output = [_, _, _, _, 4]القراءة من اليمين إلى اليسار: count[4] = 5، لذا يذهب 4 إلى الدليل 4؛ خفّض إلى 4.
وضع 2output = [_, _, 2, _, 4]count[2] = 3، لذا يذهب 2 إلى الدليل 2؛ خفّض إلى 2.
وضع 1output = [_, 1, 2, _, 4]count[1] = 2، لذا يذهب 1 إلى الدليل 1؛ خفّض إلى 1.
وضع 4output = [_, 1, 2, 4, 4]count[4] = 4، لذا يذهب هذا 4 إلى الدليل 3؛ خفّض إلى 3.
وضع 1output = [1, 1, 2, 4, 4]count[1] = 1، لذا يذهب هذا 1 إلى الدليل 0. أصبحت المصفوفة مرتّبة.

متى تستخدم فرز العد

استخدمه عندماتجنّبه عندما
تفرز أعدادًا صحيحة (أو مفاتيح قابلة للتعيين إلى أعداد صحيحة) ضمن نطاق صغير ومعروف.يكون نطاق القيم k أكبر بكثير من عدد العناصر n.
تحتاج إلى زمن خطي O(n + k) وبإمكانك تحمّل المصفوفات الإضافية.تكون الذاكرة محدودة - إذ تكلّف مصفوفة العد O(k) بغض النظر عن n.
تحتاج إلى فرز مستقر كإجراء فرعي (مثلًا داخل فرز الأساس radix sort).تكون المفاتيح أعدادًا عشرية أو سلاسل نصية أو كائنات عشوائية بلا تعيين إلى أعداد صحيحة.
تكون القيمة القصوى محدودة ورخيصة الحساب مسبقًا.يكون النطاق مجهولًا أو غير محدود، فلا يمكنك تحديد حجم مصفوفة العد.

كود Counting Sort

تنفيذ نظيف وقابل للتشغيل لخوارزمية Counting Sort بلغات Python, JavaScript, Java, C++, C. اختر لغة، وانسخ الكود، أو افتحه محمّلًا مسبقًا في ساحة تجربة Coddy.

كود Counting Sort بلغة Python

Python
1def counting_sort(a):2    # Works for non-negative integers with a small max value3    counts = [0] * (max(a) + 1)4    for value in a:5        counts[value] += 16    # Prefix sums turn counts into final positions7    for i in range(1, len(counts)):8        counts[i] += counts[i - 1]9    out = [0] * len(a)10    for value in reversed(a):  # reversed keeps equal values stable11        counts[value] -= 112        out[counts[value]] = value13    return out14
15
16nums = [4, 2, 2, 8, 3, 3, 1]17print("Before:", nums)18print("After: ", counting_sort(nums))
شغّل هذا الكود في ساحة تجربة Python

الأسئلة الشائعة حول فرز العد

ما هو التعقيد الزمني لفرز العد؟
فرز العد هو O(n + k)، حيث n هو عدد العناصر وk هو نطاق القيم الممكنة. عندما يكون k = O(n) فإن هذا زمن خطي. ويستخدم مساحة إضافية بمقدار O(n + k).
هل فرز العد مستقر؟
يمكن أن يكون كذلك. تبني النسخة المستقرة مجاميع بادئة للأعداد وتضع العناصر من اليمين إلى اليسار، مما يحافظ على الترتيب النسبي للمفاتيح المتساوية. أما النسخة البسيطة "إعادة الكتابة حسب القيمة" المعروضة هنا فتُنتج فرزًا صحيحًا لكنها تُستخدم أساسًا للأعداد الصحيحة البسيطة.
متى ينبغي أن أستخدم فرز العد؟
استخدمه عند فرز أعداد صحيحة (أو مفاتيح قابلة للتعيين إلى أعداد صحيحة) ضمن نطاق صغير ومعروف. إذا كان نطاق القيم k أكبر بكثير من عدد العناصر، فإن مصفوفة العد تهدر الذاكرة ويكون الفرز بالمقارنة أفضل.
ما الفرق بين فرز العد وفرز الأساس (radix sort)؟
يفرز فرز العد حسب القيمة الكاملة في مرور واحد ويحتاج إلى مصفوفة عد بحجم نطاق القيم. أما فرز الأساس فيفرز رقمًا رقمًا وعادة ما يستدعي فرز عد مستقرًا لكل رقم، مما يبقي النطاق لكل مرور صغيرًا (مثلًا 10 للأرقام العشرية). يتعامل فرز الأساس مع نطاقات قيم كبيرة تجعل فرز عد واحد غير عملي.
لماذا لا يكون فرز العد أسرع دائمًا من الفرز السريع (quicksort)؟
فرز العد هو O(n + k)، لذا فهو يتفوق فقط عندما يكون نطاق القيم k مقاربًا لـ n. إذا كان k ضخمًا - مثلًا فرز 100 قيمة في النطاق من 0 إلى 1,000,000,000 - فإن مصفوفة العد ذات O(k) تهيمن وتهدر الذاكرة، بينما يبقى فرز بالمقارنة بتعقيد O(n log n) مثل الفرز السريع سريعًا وموفّرًا للمساحة.
هل يمكن لفرز العد التعامل مع الأعداد السالبة؟
نعم، مع إزاحة صغيرة. بدلًا من فهرسة مصفوفة العد بالقيمة مباشرة، افهرسها بـ value - min، بحيث تُعيَّن أصغر قيمة إلى الدليل 0. ويصبح حجم مصفوفة العد max - min + 1. ونسيان هذه الإزاحة خطأ شائع يتسبب في الانهيار مع المدخلات السالبة.
Coddy programming languages illustration

أتقن الخوارزميات مع Coddy

ابدأ الآن