كيفية توليد رقم عشوائي في Java

⚡ ملخص ذكي

أرقام عشوائية في Java تأتي من أربعة خيارات مكتبة قياسية ذات صلة، وهي فئة Random و Math.random و ThreadLocalRandom و SecureRandom، كل منها يغطي مزيجًا مختلفًا من السرعة وأمان الخيوط والقوة التشفيرية للتطبيقات الحقيقية.

  • 🎲 فئة عشوائية: تُتيح مكتبة java.util.Random الوصول إلى الأنواع nextInt و nextLong و nextDouble، و nextBoolean للأنواع الأولية الشائعة.
  • 🧮 Math.random: تقوم الدالة Math.random بإرجاع قيمة عددية مزدوجة بين 0.0 شاملة و 1.0 غير شاملة باستخدام نسخة مشتركة من الدالة Random.
  • 🎯 نطاقات مخصصة: قم بتمرير حد إلى nextInt أو scale Math.random لتوليد قيم ضمن أي حد أدنى وأقصى.
  • 🧵 أمان الخيوط: تُعطي الدالة ThreadLocalRandom في java.util.concurrent كل سلسلة عمليات مولدها الخاص وتتفوق على مولد Random المشترك في ظل التنافس.
  • 🔐 الأمن: تقوم الدالة SecureRandom في java.security بإنتاج قيم قوية تشفيرياً لكلمات المرور والرموز المميزة ومفاتيح الجلسة.
  • 🌱 قابلية اعادة الأنتاج: إن تهيئة متغير عشوائي بقيمة طويلة ثابتة يجعل الاختبارات والمحاكاة حتمية عبر عمليات التشغيل.

كيفية توليد رقم عشوائي في Java

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

إنشاء رقم عشوائي في Java

استخدم Java توفر المكتبة القياسية أربع طرق شائعة لتوليد رقم عشوائي:

  • java.util.Random هي فئة عامة الأغراض. تُنتج قيمًا منطقية، وأعدادًا صحيحة، وأعدادًا طويلة، وأعدادًا عشرية، وأعدادًا مزدوجة من خلال طرق مثل nextInt, nextLongو nextDouble.
  • Math.random هي دالة مساعدة ثابتة تُرجع قيمة عددية مزدوجة بين 0.0 (شاملة) و1.0 (غير شاملة) باستخدام مُعامل مشترك Random داخليا.
  • ThreadLocalRandom in java.util.concurrent يمنح كل خيط مولده الخاص، لذلك لا تتنافس الخيوط المتعددة على مثيل واحد.
  • SecureRandom in java.security ينتج أرقامًا شبه عشوائية قوية من الناحية التشفيرية لكلمات المرور ومعرفات الجلسات والمفاتيح.

توضح الأقسام التالية كيفية توليد عشرة أرقام عشوائية باستخدام كل طريقة.

مثال: إنشاء رقم عشوائي باستخدام Java فئة عشوائية

يستخدم المثال الأول java.util.Random لإنتاج عشرة أعداد صحيحة في النطاق من 0 إلى 99 شاملةً:

import java.util.Random;

public class RandomNumbers {
    public static void main(String[] args) {
        Random objGenerator = new Random();
        for (int iCount = 0; iCount < 10; iCount++) {
            int randomNumber = objGenerator.nextInt(100);
            System.out.println("Random No : " + randomNumber);
        }
    }
}

الإخراج:

Random No : 17
Random No : 57
Random No : 73
Random No : 48
Random No : 68
Random No : 86
Random No : 34
Random No : 97
Random No : 73
Random No : 18

مثال على Random يتم إنشاؤها كـ objGenerator. nextInt(int bound) تُرجع هذه الطريقة قيمة بين 0 (شاملة) والحد المُحدد (غير شامل)، لذا nextInt(100) تُنتج قيمًا من 0 إلى 99. ولجعل التسلسل قابلاً للتكرار، مرر قيمة ابتدائية إلى الدالة البانية، على سبيل المثال new Random(42); نفس البذرة تنتج دائمًا نفس التسلسل، وهو أمر مفيد في الاختبارات والمحاكاة.

مثال: الاستخدام Java Math.random

لتوليد عشر قيم عشوائية في النطاق من 0.0 إلى 1.0، استدعِ الدالة الثابتة Math.random طريقة داخل حلقةهذه هي أسرع طريقة للحصول على عدد عشري عشوائي دون إنشاء كائن مولد:

public class DemoRandom {
    public static void main(String[] args) {
        for (int xCount = 0; xCount < 10; xCount++) {
            System.out.println(Math.random());
        }
    }
}

الإخراج:

0.46518450373334297
0.14859851177803485
0.5628391820492477
0.6323378498048606
0.1740198445692248
0.9140544122258946
0.9167350036262347
0.49251219841030147
0.7426056725722353
0.2039418871298877

تحت غطاء محرك السيارة، Math.random يُنشئ ثابتًا واحدًا Random عند الاستخدام الأول والمكالمات nextDouble هذا المولد المشترك الوحيد مناسب للعروض التوضيحية، ولكنه يصبح عنق زجاجة تحت ضغط الحمل المتعدد الخيوط الثقيل، وهو ما تتناوله الأقسام التالية.

توليد رقم عشوائي ضمن نطاق مخصص

تحتاج البرامج الحقيقية عادةً إلى قيمة عشوائية ضمن نطاق محدد، مثل عدد صحيح بين 1 و6 لرمي النرد، أو عدد عشري بين 10.0 و20.0 للمحاكاة. ويغطي نمطان جميع الحالات تقريبًا.

عدد صحيح عشوائي بين الحد الأدنى والحد الأقصى (شاملًا):

import java.util.Random;

public class RangeDemo {
    public static void main(String[] args) {
        Random rnd = new Random();
        int min = 1;
        int max = 6;
        // nextInt(max - min + 1) returns 0..(max-min), then shift by min
        int dice = rnd.nextInt(max - min + 1) + min;
        System.out.println("Dice roll : " + dice);
    }
}

عدد عشوائي مضاعف بين الحد الأدنى والحد الأقصى:

double min = 10.0;
double max = 20.0;
double value = min + (max - min) * Math.random();
System.out.println("Value : " + value);

يعمل كلا النمطين عن طريق أخذ قيمة من النطاق الأصلي للمولد، وتوسيع نطاقها ليتناسب مع عرض النطاق المستهدف، ثم إزاحتها لأعلى بالحد الأدنى. إذا كنت على Java في الإصدار 8 أو أحدث، تتوفر نفس النتيجة من خلال الإصدار الأحدث ints, longsو doubles طرق البث على Random، على سبيل المثال rnd.ints(10, 1, 7).forEach(System.out::println) لعشر رميات نرد.

ThreadLocalRandom للمعالجة متعددة الخيوط Code

مشترك واحد Random هو آمن للاستخدام المتزامن ولكنه بطيء في ظل التنافس لأن كل خيط يجب أن يتزامن على نفس البذرة الداخلية. ThreadLocalRandom، أضيفت في Java 7 ويعيش في java.util.concurrent، مما يمنح كل خيط مولده الخاص، وبالتالي لا يوجد تنازع على القفل.

import java.util.concurrent.ThreadLocalRandom;

public class ThreadLocalDemo {
    public static void main(String[] args) {
        // random int in [1, 100]
        int i = ThreadLocalRandom.current().nextInt(1, 101);

        // random long in [1000, 5000]
        long l = ThreadLocalRandom.current().nextLong(1000L, 5001L);

        // random double in [0.0, 1.0)
        double d = ThreadLocalRandom.current().nextDouble();

        System.out.println(i + " " + l + " " + d);
    }
}

استعمل ThreadLocalRandom.current() داخل سلاسل العمل، والتدفقات المتوازية، وأي مهمة متزامنة. لاحظ أن setSeed يلقي UnsupportedOperationExceptionلذا، فإنّ عملية البذر من أجل التكرار غير مدعومة؛ إذا كنت بحاجة إلى بذرة ثابتة، فالتزم بالبذرة العادية. Random.

SecureRandom للتوليد العشوائي الحساس أمنياً Numbers

حالات Random و ThreadLocalRandom ليست آمنة تشفيرياً. فبمجرد الحصول على بضع عينات من المخرجات، يستطيع المهاجم إعادة بناء الحالة الداخلية والتنبؤ بالقيم المستقبلية. بالنسبة لكلمات المرور، ورموز الجلسة، وقيم الملح، ومفاتيح التشفير، استخدم java.security.SecureRandom بدلاً من ذلك، يستمد مواده الأولية من مخزون الإنتروبيا لنظام التشغيل وينتج قيماً يصعب التنبؤ بها.

import java.security.SecureRandom;

public class SecureDemo {
    public static void main(String[] args) {
        SecureRandom secure = new SecureRandom();

        byte[] token = new byte[16];
        secure.nextBytes(token);          // 128-bit random token

        int otp = secure.nextInt(1_000_000); // 6-digit one-time code

        System.out.println("OTP : " + String.format("%06d", otp));
    }
}

SecureRandom أبطأ من Random لأنه يستخدم خوارزميات أقوى ومواد أولية أكثر ثراءً. خصصه لأعمال الأمن واستخدم المولدات الأسرع للألعاب والمحاكاة وبيانات الاختبار.

الأسئلة الشائعة

أنشئ نسخة من java.util.Random واستدعِ nextInt و nextLong و nextDoubleأو nextBoolean. وللحصول على سطر واحد سريع، استخدم Math.random الذي يُرجع قيمة عددية مزدوجة بين 0.0 و1.0. كلا الطريقتين متوفرتان في المكتبة القياسية.

استخدم الدالة random.nextInt(max – min + 1) + min للأعداد الصحيحة، أو min + (max – min) * Math.random() للأعداد العشرية. تعمل هذه الصيغ على تغيير نطاق المولد ليناسب النافذة المستهدفة، مع إزاحته بمقدار القيمة الدنيا.

Random هي فئة كاملة تحتوي على دوال للعديد من الأنواع الأولية وتدعم التهيئة. Math.random هي دالة مساعدة ثابتة تُرجع قيمة عددية مزدوجة بين 0.0 و1.0. داخليًا، تستدعي Math.random نسخة واحدة مشتركة من Random.

نعم، لكن الاستدعاءات المتزامنة تتنافس على نفس القيمة الأولية الداخلية، مما يُبطئ التعليمات البرمجية ذات الخيوط المتعددة. استخدم ThreadLocalRandom.current() من java.util.concurrent لمنح كل خيط مولده الخاص وإزالة هذا التنافس.

مرر قيمة ثابتة من نوع long إلى مُنشئ Random، على سبيل المثال new Random(42)، أو استدعِ setSeed(42). سيُنتج المُولِّد نفس التسلسل في كل تشغيل، وهو أمر ضروري لإجراء اختبارات ومحاكاة قابلة للتكرار.

استخدم SecureRandom عندما يجب أن تكون القيمة مقاومة للتنبؤ، مثل كلمات المرور، ورموز الجلسات، وقيم التشفير، والرموز لمرة واحدة، ومفاتيح التشفير. أما Plain Random فهو سريع ولكنه قابل للتنبؤ، لذا فهو غير آمن للاستخدامات الأمنية.

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

نعم. يقوم GitHub Copilot بإنشاء قوالب جاهزة لمقاطع Random وThreadLocalRandom وSecureRandom، بما يتوافق مع صيغ قياس النطاق، و JUnit الاختبارات. Revراجع كل اقتراح للتأكد من أن الفئة المختارة تناسب حالة الاستخدام، وخاصة بالنسبة للتعليمات البرمجية الحساسة أمنياً.

تلخيص هذه التدوينة بـ: