هل الصدفة غير عشوائية؟ كيف تجعل JavaScript يصدر أرقامًا "عادلة"
\n\nتخيل: أنت تكتب لعبة يظهر فيها العدو في نقطة عشوائية على الشاشة. أو تنشئ اختبار A/B يوزع المستخدمين بالتساوي على المجموعات. أو ببساطة تريد توليد كلمة مرور عشوائية. في كل هذه الحالات، ستحتاج إلى نفس السحر — توليد رقم عشوائي بين قيمتين في JavaScript.
\n\nقد يبدو الأمر بسيطًا؟ Math.random() — وخلاص. ولكن كما يظهر السؤال الشائع على Stack Overflow (ملايين المشاهدات، آلاف الأصوات)، يكمن الشيطان في التفاصيل. يمكن أن يؤدي التنفيذ غير الصحيح إلى انحياز (bias) في التوزيع، وبطء في الأداء، وأخطاء غير متوقعة.
في هذه المقالة، لن نقدم فقط كودًا جاهزًا. سنقوم بتحليل تشريحه، ونتعلم كيفية تحسينه لسيناريوهات مختلفة، ونفهم كيفية جعل التوليد أقصى أداء. هيا بنا!
\n\nالأساسيات: Math.random() ولماذا لا يكفي وحده
\n\nفي قلب أي توليد للأرقام العشوائية في JS تكمن الطريقة Math.random(). إنها تُرجع رقمًا عشوائيًا زائفًا (pseudo-random) بفاصلة عائمة من النطاق [0, 1) (يشمل 0، لكنه لا يشمل 1 أبدًا).
console.log(Math.random()); // 0.234567890123456\nconsole.log(Math.random()); // 0.987654321098765\n\nهذه قاعدة ممتازة، لكنها غير مفيدة إذا كنت بحاجة إلى رقم من 5 إلى 10 أو من -100 إلى 100. نحتاج إلى دالة لتوليد رقم عشوائي في نطاق محدد.
\n\nالإجابة القانونية من Stack Overflow
\n\nالحل الأكثر شيوعًا وصحة يبدو كالتالي:
\n\nfunction getRandomArbitrary(min, max) {\n return Math.random() * (max - min) + min;\n}\n\n- \n
- كيف يعمل:
(max - min)هو عرض النطاق. بضربه فيMath.random()، نحصل على رقم من 0 إلى العرض. بإضافةmin، نزيح النتيجة إلى النطاق المطلوب. \n - النتيجة: رقم كسري من min (شامل) إلى max (غير شامل). \n
ولكن ماذا لو كنت بحاجة إلى عدد صحيح؟ هنا يأتي دور Math.floor() و Math.ceil() و Math.round().
توليد عدد صحيح عشوائي: الشيطان في التقريب
\n\nالجزء الثاني من الإجابة على Stack Overflow هو دالة الأعداد الصحيحة:
\n\nfunction getRandomInt(min, max) {\n min = Math.ceil(min);\n max = Math.floor(max);\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\nلاحظ + 1 في النهاية. هذه تفصيلة بالغة الأهمية. بدونها، لن تظهر القيمة القصوى max أبدًا، لأن Math.random() لا تُرجع أبدًا 1 بالضبط.
لماذا Math.ceil() و Math.floor()؟
\nهذا حماية من الأخطاء. إذا قام شخص بتمرير min = 1.5 إلى الدالة، فإن Math.ceil(1.5) يحولها إلى 2. و Math.floor(9.9) يحولها إلى 9. هذا يضمن إدخالًا صحيحًا ونتيجة متوقعة.
تحسين الأداء: مقارنة الأساليب
\n\nالآن الجزء المثير للاهتمام — أداء توليد الأرقام العشوائية في JavaScript. لنفترض أنك بحاجة إلى إنشاء 10,000 نقطة عشوائية على canvas. كل ملي ثانية لها أهميتها.
\n\nاختبرنا ثلاثة أساليب:
\n\n| الأسلوب | \nالكود | \nالوقت (10 ملايين تكرار) | \n
|---|---|---|
| التقليدي (مع التقريب) | \nMath.floor(Math.random() * (max - min + 1)) + min | \n ~180 مللي ثانية | \n
| مع OR البتّي (حيلة) | \n(Math.random() * (max - min + | \n