كيفية خلط قائمة في C#: من مبتدئ إلى مستخدم واثق
\n\nتخيل موقفاً: أنت تكتب برنامجاً لمسابقة، ويجب أن تظهر الأسئلة بترتيب عشوائي. أو ربما تقوم بعمل تطبيق لسحب الجوائز، حيث يحصل كل مشارك على رقم عشوائي. أو ربما تريد فقط أن تعمل قائمة التشغيل الموسيقية بشكل عشوائي، وليس أبجدياً. في كل هذه الحالات، تواجه نفس المهمة: كيفية خلط قائمة (List<T>) في C#؟
\n\nالسؤال شائع جداً لدرجة أنه حصل على آلاف المشاهدات ومئات الإجابات على Stack Overflow. وهذا ليس مفاجئاً - على الرغم من أن المهمة تبدو بسيطة، إلا أن لها مخاطر خفية. في هذه المقالة، سنستعرض ثلاث طرق شائعة لعشوائية القوائم في C#، من الأبسط إلى الأكثر فعالية. ستتعلم كيف يعمل كل طريقة، ومتى تستخدمها، والأهم من ذلك، كيف تتجنب الأخطاء الشائعة.
\n\nلماذا لا يمكنك ببساطة الفرز عشوائياً؟
\n\nأول ما يتبادر إلى ذهن المبتدئ: "دعني أفرز القائمة بشكل عشوائي!" يبدو الأمر كالتالي:
\n\nList<string> myList = new List<string> { "تفاحة", "موز", "كرز", "شمام" };\nRandom rnd = new Random();\nvar shuffled = myList.OrderBy(item => rnd.Next()).ToList();\n\n\nالكود يعمل. لكن فقط للوهلة الأولى. المشكلة هي أن Random.Next() يُستدعى مرة واحدة لكل عنصر، وتتم المقارنة بناءً على رقم عشوائي واحد. هذا لا يضمن توزيعاً متساوياً. علاوة على ذلك، يمكن أن يؤدي هذا الأسلوب إلى انحياز (bias) - بعض العناصر قد تظهر في بداية أو نهاية القائمة بشكل متكرر. بالنسبة للتطبيقات الجادة (مثل الحسابات العلمية أو الألعاب)، هذا غير مقبول.
لكن لا تتسرع في رفض هذه الطريقة! بالنسبة للمهام البسيطة حيث الدقة ليست حرجة، فهي مقبولة. المهم أن تتذكر حدودها.
\n\nالطريقة رقم 1: خوارزمية فيشر-يتس (المعروفة أيضاً بـ Knuth Shuffle) - المعيار الذهبي
\n\nإذا كنت تريد القيام بالأمر بشكل صحيح من المرة الأولى، استخدم خوارزمية فيشر-يتس. هذه هي الطريقة الكلاسيكية لخلط مصفوفة أو قائمة، المستخدمة في البرمجة منذ عام 1938. تعمل بزمن O(n) وذاكرة إضافية O(1) - أي أنها سريعة وفعالة جداً.
\n\nإليك كيف تبدو في C#:
\n\nusing System;\nusing System.Collections.Generic;\n\npublic static class ListExtensions\n{\n private static readonly Random rng = new Random();\n\n public static void Shuffle<T>(this IList<T> list)\n {\n int n = list.Count;\n while (n > 1)\n {\n n--;\n int k = rng.Next(n + 1);\n T value = list[k];\n list[k] = list[n];\n list[n] = value;\n }\n }\n}\n\n\nكيف يعمل هذا؟
\n- \n
- نبدأ من آخر عنصر في القائمة. \n
- نختار فهرساً عشوائياً من 0 إلى الموضع الحالي. \n
- نتبادل العنصر في الموضع الحالي مع العنصر في الفهرس العشوائي. \n
- ننتقل إلى العنصر السابق ونكرر حتى نصل إلى البداية. \n
لماذا هذا هو الخيار الأفضل؟
\n- \n
- التساوي: كل تبديل للعناصر متساوي الاحتمال. \n
- الكفاءة: تمريرة واحدة عبر القائمة، لا عمليات زائدة. \n
- يعمل في المكان: لا ينشئ قوائم جديدة، مما يوفر الذاكرة. \n
- العالمية: يعمل مع أي نوع
IList<T>، بما في ذلك المصفوفات. \n
الاستخدام بسيط:
\n\nList<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };\nnumbers.Shuffle();\n// الآن numbers بترتيب عشوائي\n\n\nلاحظ: لقد أعلنا