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

كيف يحسب أعمال السورت

ويستغل عد شركة " سرت " المعرفة بأن قيم المدخلات هي متجانسات مستمدة من نطاق صغير . وبدلا من مقارنات ثنائية، فإنه يبني مقياساً متواتراً للقيم ثم يستخدم تلك البرمجيات لوضع كل عنصر في موقعه الصحيح المصنَّف.

النهج الأساسي: إعادة الإعمار المباشر

أبسط نسخة من الكونت سورت تعمل في تصاريح دخول

  1. ترددات البلدان - تُحدث عبر صفيفة المدخلات وتُزيد من قيمة كل قيمة تراها.
  2. Overwrite the input] – Walk through the counter array from smallest to largest and, for each value, write it back into the input array as many times as its count.

وينتج هذا ناتجاً مصنَّفاً ولكنه لا يحفظ النظام النسبي للازدواجية (ليس مستقراً) بل يحفظ النظام الأصلي للسجلات بمفاتيح متساوية، أما البديل المستقر، الذي يوصف لاحقاً، فهو الأكثر شيوعاً في الممارسة العملية.

البديل: الكونتات التراكمية

لجعل العدسات مستقرة، نضيف تمريرة ثالثة:

  1. عدّ الترددات كما كانت من قبل
  2. (ب) تحويل صفيفة التردد إلى مجموعة عد تراكمية.() وبعد هذه الخطوة، ] يُحتفظ بعدد العناصر ”i.
  3. (ج) أن تُدرج مجموعة المدخلات في الاتجاه المعاكس (من العنصر الأخير إلى العنصر الأول) - بالنسبة لكل عنصر، تستخدم عدها التراكمي لإيجاد موقعها في صفيفة النواتج، ووضعها، وإلغاء العد.

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

تنفيذ نظام العد التنازلي في الفئة جيم(ب)

وفيما يلي تنفيذان من الفئة " جيم " : النسخة الأساسية في الموقع (للمناطق التي لا ضرورة فيها للاستقرار) والصيغة المستقرة التي تستخدم صفيفة مساعدة، وكلتاهما يتطلبان معرفة القيمة القصوى مسبقا.

أساسي (مستقرة)

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

public static void CountingSortBasic(int[] array, int maxValue)
{
 int[] counts = new int[maxValue + 1];

 // Count each element's frequency
 for (int i = 0; i < array.Length; i++)
 {
 counts[array[i]]++;
 }

 // Overwrite the original array in sorted order
 int index = 0;
 for (int value = 0; value <= maxValue; value++)
 {
 while (counts[value]-- > 0)
 {
 array[index++] = value;
 }
 }
}

العد التنازلي

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

public static int[] CountingSortStable(int[] array, int maxValue)
{
 int[] counts = new int[maxValue + 1];
 int[] output = new int[array.Length];

 // Step 1: Count occurrences
 foreach (int num in array)
 {
 counts[num]++;
 }

 // Step 2: Transform counts to cumulative counts
 for (int i = 1; i <= maxValue; i++)
 {
 counts[i] += counts[i - 1];
 }

 // Step 3: Build the output array (iterate input in reverse for stability)
 for (int i = array.Length - 1; i >= 0; i--)
 {
 int value = array[i];
 output[counts[value] - 1] = value;
 counts[value]--;
 }

 return output;
}

وفي كلا التنفيذين، هو أكبر مبرد يظهر في الصفيفة، وإذا كان الحد الأقصى الحقيقي غير معروف، فيمكنكم أن تحسبوه بمسح تحضيري (O(n)). وتعيد النسخة المستقرة صفيفة جديدة مصنّفة، وتتركون النسخة الأصلية دون تغيير.

تحليل التعقيد

Let n be the number of elements and k] = max - min + 1 (the range of possible values).

  • Time:] countinging Sort runs in O(n + k) time. The countinging phase is O(n), the cumulative prefix is O(k), and the reconstruction is O(n). When k is O(n), the algorithm is linear.
  • Space:] The basic version uses O(k) extra space for the count array. The stable version uses O(n + k) because it also allocates the output array. This makes counting Sort unsuitable when the range is large relative to the number of items.
  • Comparison with other sorts:] Comparison —based sorts like QuickSort and MergeSort require at least O(n log n) comparisons. For small k (e.g., k < 10,000 and n > 100,000), countinging Sort can be orders of magnitude faster.

الفرق والتمديدات

معالجة المبردات السلبية

(ب) العمل في مجال العد التنازلي للثروة المحلية مع البخار غير المجهول، ولتناول القيم السلبية، يُحوّل النطاق بأكمله بحيث يصبح الحد الأدنى صفراً، مثلاً، إذا تراوحت الأرقام بين 000 1 و000 100، يقابل كل عنصر بـ 000 1، وعندها يكون حجم مجموعة العد هو .

public static int[] CountingSortWithNegative(int[] array)
{
 if (array.Length == 0) return array;

 int min = array.Min();
 int max = array.Max();
 int range = max - min + 1;

 int[] counts = new int[range];
 int[] output = new int[array.Length];

 foreach (int num in array)
 counts[num - min]++;

 for (int i = 1; i < range; i++)
 counts[i] += counts[i - 1];

 for (int i = array.Length - 1; i >= 0; i--)
 {
 int value = array[i];
 output[counts[value - min] - 1] = value;
 counts[value - min]--;
 }

 return output;
}

Mapping Non-Integer Keys

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

Radix Sort Combo

(راديكس سورت) يرقم (أو يقطع) بشكل فردي، ويعد العد التنازلي هو الخيار الطبيعي لكل تمرير عندما تكون القاعدة (مثل 10 أو 256) صغيرة، وهذا يتيح فرز المبردات التعسفية في وقتها، وليس فقط الصغيرة.

الاعتبارات العملية في جيم(ب)

Memory Footprint and Large k

The largest holefall is allocating a count array larger than the available memory. for example, sorting 1,000 elements with a range of 1,000,000 wastes space. always verify that k is not orders of magnitude larger than ]n -otherwise use a comparison or sort a hybrid approach.

المواظبة والروحية؛

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

حالات الإدج

  • Empty array] - return immediately.
  • عنصر مُغنطيسي ] - الفرز هو ثلاثي.
  • All similar values] — the count array has one non-zero entry; reconstruction runs in O(n).
  • Large range but sparse data] – countinging Sort becomes inefficient because most count entries are zero. Consider a hash‐based counting approach or Bucket Sort.

توصيات الأداء

استخدام العد التنازلي عندما تعرف أن المدخلات تقع في نطاق صغير (مثل الصفوف من 0 إلى 100، أو الصفوف من صفر إلى 120، أو رموز الخطأ من 0 إلى 255). بالنسبة للسلاسل الكبيرة، النظر في راديك سورت أو الهجين الذي يعود إلى كويكسورت للتجزئة العالية المدى.

متى تستخدم العدة العذب (وعندما لا يكون ذلك)

SituationRecommendation
Small integer range (k ~ n)Excellent choice – linear time, simple code.
Large integer range (k >> n)Avoid – memory waste and O(k) overhead.
Need stabilityUse the stable variant (cumulative counts).
Strings or objectsConsider Radix Sort or a comparison sort.
Extremely large datasetsCounting Sort can be parallelized; but watch memory.

تحديد المعايير والأداء

In a typical standard criterion with n = 1,000,000 and k = 1,000, countinging Sort completes in about 20 -30% of the time taken by (which uses introsort). The gap widens as k decreases. Below is an approximate comparison (execution times on a modern CPU with.NET 8):

n = 1,000,000 | k = 1,000
Array.Sort (QuickSort variant) : 68 ms
CountingSortBasic : 12 ms
CountingSortStable : 18 ms

وعندما ينمو النطاق إلى 000 10، لا يزال عدد المحسوبين في الفرز يفوز، ولكن هامشه ضيق، وبالنسبة لـ 000 100 شخص، يبدأ رأس الذاكرة (400 كيلو بيزو للمجموعة المحسوبة) في إيذاء خدّة وحدة تحليل البرامج، ويمكن أن يتحلل الأداء.

خاتمة

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

For further reading, consult the Wikipedia article on countinging Sort], the ]Microsoft docs on Array.Sort], and a practical guide from GeeksforGeeks.