כאשר משימת מיון שלך כוללת מגוון רחב של פולשים קטנים - כגון ציונים, גילים, או קודים קטגוריאליים - אלגוריתמים המבוססים על השוואה קלאסית כמו QuickSort או MergeSort יכולים להרגיש כמו overkill. אלגוריתמים אלה לרוץ ב O(n di n) זמן, אבל אם טווח ערכים אפשריים מוגבל, אתה יכול למיין בזמן ליניארי O(n + k) עם FLT:0 מנפח זה של חומר זה יכול להיות מנפח את זה לא יציב, במקום למיין את זה, כלומר, כלומר, כלומר, כלומר, זה סוג 1 אלגוריתם פשוט לא יציב של ערכים פשוטים, במקום למיין את זה הוא פשוט יותר מאשר למיין את זה הוא אלגוריתם זה הוא פשוט אלגוריתם ברור של ערכים אפשריים הוא פשוט אלגוריתם ברור יותר מאשר למיין את זה הוא פשוט אלגוריתם ברור של ערכים ברורים יותר מאשר אלגוריתם ברור של ערכים סביר יותר מאשר אלגוריתם ברור של ערכים פשוטים זה הוא אלגוריתם ברור של ערכים אפשריים הוא פשוט אלגוריתם ברור 1 אלגוריתם ברור יותר מאשר אלגוריתם ברור כי הוא מוגבל, במקום אלגוריתם ברור של ערכים אפשריים הוא פשוט אלגוריתם זה הוא פשוט אלגוריתם זה הוא מוגבל, מאשר אלגוריתם זה הוא פשוט אלגוריתם ברור כי הוא אלגוריתם זה הוא מוגבל, במקום אלגוריתם זה הוא פשוט

איך לספור עבודות מין

ספירת המיון מנצלת את הידע שערכי הקלט נמשכים מטווח קטן (FLT:0) במקום השוואות בוהקות, היא בונה תדר של הערכים ולאחר מכן משתמשת כי הגולגולת כדי להציב כל אלמנט בעמדה המתוקנת שלה.

גישה בסיסית: שיקום ישיר

הגרסה הפשוטה ביותר של ספירת מין עובדת בשני חולות:

  1. (ב) ,0) ,7 , ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
  2. (ב) ,0) לקדש את הקלט של ה- 1:1 - לעבור את מערך הדלפק מקטן לגדול ביותר, ולכל ערך, לכתוב אותו בחזרה לתוך מערך הקלט כמה פעמים כספירת שלו.

זה מניב פלט ממונן אך עושה את זה:0 (לא מובנים 1:0) לשמור על הסדר היחסי של לשכפלות (זה לא יציב) נושאים יציבות כאשר אתה ממיין מפתח תוך שמירה על הסדר המקורי של רשומות עם מפתחות שווים.

The Stable Variant: Cumulative Counts

כדי להפוך את ספירת המיון יציב, אנו מוסיפים מעבר שלישי:

  1. לספור תדרים כמו קודם.
  2. אִם הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא הוּא .
  3. החל את מערך הקלט הפוך (מרכיב אחרון תחילה) עבור כל אלמנט, השתמש ספירת המצטברת שלו כדי למצוא את עמדתה במערך הפלט, להציב אותו, ולהרוס את הספירה.

מכיוון שאנו חוצים את ההפוך, הסדר היחסי של אלמנטים שווים נשמר.המערך הפלט נפרד מהקלט, כך גרסה זו משתמשת במרחב נוסף של O(n) לפלט, בעוד שהגרסה הבסיסית יכולה למיין במקום על ידי כתיבת יתר של הקלט.

המונחים: Counting type in C#

להלן שני יישומי C#: הגרסה הבסיסית של מקום (לתרחישים שבהם יציבות היא מיותרת) והגרסה היציבה המשתמשת במערך עזרי.שנים דורשים לדעת את הערך המקסימלי מראש.

בסיס (Non-Stable) Counting

גרסה זו היא סוג של מערך קלט ישירות ללא buffer נוסף.זה יעיל זיכרון אבל לא יציב.

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;
}

בשני המימושים, ה-FLT:4 הוא ה- integer הגדול ביותר שמופיע במערך.אם המרבי האמיתי אינו ידוע, אתה יכול למקם אותו עם סריקה הכנה (O(n)) הגרסה היציבה מחזירה מערך חדש, משאיר את המקורי ללא שינוי.

ניתוח מורכבות

(ה) ,0 , ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇

  • (ב) ,0) ,Time:veFLT:1 , Counting sort פועל ב-FLT:2O(n + k)FLT 3: 3 הזמן; שלב הספירה הוא O(n), הקידומת המצטברת היא O(k), והשיקום הוא O(n). כאשר k הוא O(n), האלגוריתם הוא ליניארי.
  • (FLT:0)Space:BuildFLT:1) הגרסה הבסיסית משתמשת שטח נוסף של O(k) עבור מערך הספירה.הגרסה היציבה משתמשת O(n + k) כי היא גם מקצה את מערך התפוקה.זה הופך את הרוזנת לחסרת ערך כאשר הטווח גדול יחסית למספר הפריטים.
  • (FLT:0)Comparison עם סוגים אחרים:FLT:1 השוואות מבוסס סוגים כגון QuickSort ו- MergeSort דורש לפחות O(n log n) השוואות. עבור k קטן (למשל, k < 10,000 ו n > 100,000), ספירה של סוג יכול להיות פקודות של גודל מהיר יותר.

שינויים ותוספות

עקבו אחרי Negative Integers

ספירת מין עובדת עם לא-שלילית של חומרים לא-שליליים.כדי להתמודד עם ערכים שליליים, לשנות את הטווח כולו כך שהמינימום הופך אפס. לדוגמה, אם מספרים נעים בין -1000 ל-1000, מדגימים כל אלמנט ב +1000.המערך הספירה אז יש לו גודל FLT:5.

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;
}

צילום: non-Integer Keys

ספירת המיון דורשת מפתחות טטגרס.אם הנתונים שלך מורכבים מהדמויות (בייטים), או enumerations שניתן להטיל כדי integers, אתה עדיין יכול ליישם אותם. עבור אובייקטים גדולים יותר, אתה יכול לחלץ מפתח integer ולמיין את האובייקטים בהתאם - זה בדיוק איך רדינקס משתמש לעתים קרובות ספירה מונים כמו תת-ה subroutine הפנימי שלה.

רדינקס - Combo

Radix מעבד ספרות (או ביטים) באופן אישי, וספירת סוג היא הבחירה הטבעית עבור כל מעבר כאשר הבסיס (למשל, 10 או 256) הוא קטן.זה מאפשר מיון זמני ליניארי של פולשים שרירותיים, לא רק קטן.

שיקולים מעשיים ב-C#

טביעת רגל זיכרון ו-K גדול

הנפילה הגדולה ביותר היא הקצאת ספירה גדולה יותר מהזיכרון הזמין.לדוגמה, מחיקת 1,000 אלמנטים עם טווח של 1 000 פסולת שטח.תמיד לוודא כי FLT:0kekphFLT:1 אינה צו של גודל גדול יותר מאשר FLT:2nFLT 3:2nFLT 3 - שימוש חכם אחר סוג של השוואה או גישה היברידית.

שקיפות וספאן < T>

עבור מגוון גדול מאוד, אתה יכול במקביל את שלב הספירה על ידי חלוקת קלט על פני חוטים.כל חוט נחשב את הקטע שלו לתוך מערך פרטי, ולאחר מכן את התוצאות החלקיות מצטברות.שימוש ב-FLT 7 ו-FLT:8 עבור מערך הספירה יכול להפחית הקצאות הערימה כאשר הטווח הוא קטן.

אירועים

  • (ב) ויקרא י"ד: "וַיָּבְהִיתִי" (בראשית כ"ד).
  • (ב) ויקרא י"ד: "ה', ויקרא י"ד, ויקרא י"ד, ויקרא י"ד).
  • (ב) לכל הערכים האלהים של ההרחבה: למערך הספירה יש כניסה אחת שאינה אפסית; שיקום פועל ב- O(n).
  • (FLT:0)Large טווח אבל נתונים דליקים FIRLT:1) - ספירת המיון הופכת לבלתי יעילה כי רוב הספירה ערכים הם אפס.חשב גישה מבוססת-על או Bucket.

המלצות ביצועים

השתמש ב Countingמיין כאשר אתה יודע את הקלט אינטגר לתוך טווח קטן (למשל, ציונים 0-100, גיל 0-120, או קודים שגיאה 0-255). עבור טווחים גדולים יותר, לשקול רדיאקס או היברידית אשר נופלת בחזרה ל QuickSort עבור מחיצות לטווח גבוה.

מתי להשתמש בספירת מין (ומתי לא)

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.

Benchmarking and Performance

במדד טיפוסי עם n=1 000 ו- k=1,000, ספירת המיון משלימה בערך 20-30% מהזמן שנלקח על ידי FLT:9 (שמשתמשים ב introsort) הפערים רחבים כמו k יורד. להלן הוא השוואה דומה (זמני הפעלה על CPU מודרני עם .NET 8):

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

כאשר הטווח גדל ל-10,000, ספירת מין עדיין מנצחת, אבל שולי הצרים. עבור k = 100,000, הזיכרון מעל הראש ( ⁇ 400 KB עבור מערך הספירה) מתחיל לפגוע CPU cache, וביצועים יכולים לכווץ.

מסקנה

ספירת סוג היא אלגוריתם פשוט מטעה המספק ביצועים ליניאריים כאשר הנתונים מתאימים את המגבלות שלו.עבור מפתחי C# העוסקים בערכים גדולים של פולשים קטנים, זהו כלי יקר שיכול להפחית באופן דרמטי את הזמן מיון. שמור על טווח הנתונים שלך: אם הוא קטן וידוע, ספירה מון תוציא כל חלופה מבוססת השוואה.

(ב) ב[[1924]], [[1924]]]], [[1924]]]]]], [[1924]]]], [[1924]]]], [[1924]]]], [[1924]]]]]], [[1924]]]]]], [[1924]]]]]], [[1924]]]]]]]]]], [[1924]]]]]]]], [[1924]]