توليد الأعداد العشوائية في C++
تُعدّ عملية توليد الأعداد العشوائية في البرمجة من العمليات الأساسية والمهمة في العديد من التطبيقات مثل المحاكاة، الألعاب، تحليل البيانات، واختبار الخوارزميات. في لغة C++، يوجد العديد من الطرق لتوليد الأعداد العشوائية، والتي تتراوح بين الأساليب البسيطة والمباشرة إلى الطرق المعقدة التي توفر تحكمًا دقيقًا في طبيعة الأعداد العشوائية المُولدة.
1. مقدمة عن الأعداد العشوائية
الأعداد العشوائية هي أعداد لا تتبع نمطًا محددًا أو predictable، أي أن كل عدد يتم توليده يتم اختياره بشكل عشوائي من مجموعة معينة من الأعداد. في البرمجة، تُستخدم الأعداد العشوائية في مجموعة متنوعة من التطبيقات مثل:
-
محاكاة الظواهر الطبيعية.
-
إنشاء ألعاب الكمبيوتر التي تعتمد على العشوائية.
-
تحليل البيانات مثل تصنيف العينات العشوائية.
-
اختبار الخوارزميات التي تحتاج إلى بيانات غير متوقعة.
ومع ذلك، على الرغم من أن الأعداد العشوائية تُسمى كذلك، فإن هذه الأعداد في الواقع تُولد باستخدام خوارزميات رياضية تُعرف بالأعداد العشوائية الزائفة (pseudo-random numbers)، وذلك لأنه يتم تحديد قيم الأعداد بناءً على بعض المعاملات المبدئية أو “البذور” (seeds).
2. توليد الأعداد العشوائية في C++
في لغة C++، كانت عملية توليد الأعداد العشوائية تعتمد في السابق على مكتبة التي تحتوي على دالة rand() لتوليد الأعداد العشوائية. ومع ذلك، في الإصدارات الحديثة من C++ (ابتداءً من C++11)، تم تحسين آلية توليد الأعداد العشوائية ليصبح أكثر أمانًا وتنوعًا، حيث تم إضافة مكتبة التي توفر مجموعة من الأدوات المتقدمة لتوليد الأعداد العشوائية.
2.1 استخدام دالة rand() (في الإصدارات القديمة)
في الإصدارات القديمة من C++، كانت دالة rand() هي الأداة الرئيسية لتوليد الأعداد العشوائية. وتعمل هذه الدالة على توليد عدد صحيح عشوائي يتراوح بين 0 و RAND_MAX، وهو ثابت مُعرف في المكتبة ويمثل الحد الأقصى للقيمة التي يمكن أن تولدها الدالة.
مثال:
cpp#include
#include
#include
int main() {
// تعيين بذرة العشوائية باستخدام الوقت الحالي
srand(time(0));
// توليد عدد عشوائي بين 0 و RAND_MAX
int random_number = rand();
std::cout << "Random Number: " << random_number << std::endl;
// توليد عدد عشوائي بين 0 و 99
int random_between_0_and_99 = rand() % 100;
std::cout << "Random Number between 0 and 99: " << random_between_0_and_99 << std::endl;
return 0;
}
في المثال أعلاه، تم استخدام دالة srand() لتعيين بذرة العشوائية باستخدام الوقت الحالي، مما يضمن توليد أرقام عشوائية مختلفة في كل مرة يتم فيها تشغيل البرنامج. ثم استخدمنا دالة rand() لتوليد الأعداد العشوائية.
2.2 توليد الأعداد العشوائية في C++11 وما بعده باستخدام مكتبة
مع إصدار C++11، تم إضافة مكتبة التي توفر أدوات أكثر تقدمًا لتوليد الأعداد العشوائية. هذه المكتبة تسمح بتحديد نوع الأعداد العشوائية بدقة أكبر، مثل الأعداد العشرية العشوائية أو الأعداد العشوائية من توزيعات معينة (مثل التوزيع العادي أو التوزيع المتساوي). تتمثل أكبر مزايا هذه المكتبة في توفير توليد أعداد عشوائية أكثر قوة وموثوقية.
مثال:
cpp#include
#include
#include
int main() {
// إنشاء محرك عشوائي باستخدام الوقت الحالي
std::default_random_engine generator(time(0));
// تعريف التوزيع العشوائي (توزيع منتظم بين 0 و 99)
std::uniform_int_distribution<int> distribution(0, 99);
// توليد 5 أعداد عشوائية
for (int i = 0; i < 5; i++) {
int random_number = distribution(generator);
std::cout << "Random Number: " << random_number << std::endl;
}
return 0;
}
في هذا المثال، قمنا بإنشاء محرك عشوائي من نوع default_random_engine، والذي يتم تهيئته ببذرة عشوائية باستخدام الوقت الحالي. ثم استخدمنا التوزيع المنتظم uniform_int_distribution لتحديد نطاق الأعداد العشوائية التي سيتم توليدها (من 0 إلى 99). هذه الطريقة أكثر دقة ومرونة من استخدام rand().
2.3 أنواع المحركات والتوزيعات في مكتبة
تقدم مكتبة العديد من المحركات والتوزيعات التي يمكن استخدامها وفقًا لاحتياجات البرنامج. فيما يلي بعض المحركات والتوزيعات المتاحة:
-
محركات العشوائية:
-
std::default_random_engine: محرك عشوائي افتراضي. -
std::mt19937: محرك ماتياس تانغ للمولد العشوائي، ويعتبر أكثر كفاءة في توليد الأعداد العشوائية. -
std::ranlux24: محرك راندلوكس 24.
-
-
التوزيعات العشوائية:
-
std::uniform_int_distribution: لتوزيع الأعداد العشوائية الصحيحة (integer) بشكل منتظم. -
std::uniform_real_distribution: لتوزيع الأعداد العشوائية العشرية (floating-point) بشكل منتظم. -
std::normal_distribution: لتوزيع الأعداد العشوائية وفقًا لتوزيع طبيعي (Gaussian). -
std::bernoulli_distribution: لتوزيع الأعداد العشوائية وفقًا لتوزيع برنولي.
-
مثال:
cpp#include
#include
#include
int main() {
// إنشاء محرك عشوائي
std::default_random_engine generator(time(0));
// توليد أعداد عشوائية وفق توزيع عادي (Gaussian)
std::normal_distribution<double> distribution(0.0, 1.0); // المتوسط = 0 والانحراف المعياري = 1
// توليد 5 أعداد عشوائية
for (int i = 0; i < 5; i++) {
double random_number = distribution(generator);
std::cout << "Random Number (Normal Distribution): " << random_number << std::endl;
}
return 0;
}
في هذا المثال، استخدمنا التوزيع العادي std::normal_distribution لتوليد أعداد عشوائية تتبع التوزيع الطبيعي مع متوسط 0 وانحراف معياري 1. هذه الطريقة مفيدة في التطبيقات التي تتطلب أعدادًا عشوائية تتوزع بشكل طبيعي، مثل محاكاة الظواهر الطبيعية.
3. استخدام البذور (Seeds) في توليد الأعداد العشوائية
أحد الجوانب الهامة في توليد الأعداد العشوائية هو استخدام البذور (seeds). البذرة هي قيمة تُستخدم لتحديد الحالة الأولية للمولد العشوائي، وتلعب دورًا كبيرًا في التأثير على تسلسل الأعداد العشوائية المُولدة. إذا كانت البذرة ثابتة، فسيتم توليد نفس تسلسل الأعداد العشوائية في كل مرة يتم فيها تشغيل البرنامج، مما قد يكون غير مرغوب فيه في بعض الحالات.
لتجنب توليد نفس الأرقام العشوائية في كل مرة يتم فيها تشغيل البرنامج، عادةً ما يتم استخدام الوقت الحالي كنقطة انطلاق للبذرة باستخدام دالة time(0) أو دوال مشابهة.
مثال:
cpp#include
#include
#include
int main() {
// تعيين بذرة العشوائية باستخدام الوقت الحالي
std::default_random_engine generator(time(0));
// توليد عدد عشوائي باستخدام توزيع منتظم
std::uniform_int_distribution<int> distribution(1, 100);
std::cout << "Random Number between 1 and 100: " << distribution(generator) << std::endl;
return 0;
}
4. التحديات والاعتبارات عند استخدام الأعداد العشوائية
في حين أن توليد الأعداد العشوائية قد يبدو عملية بسيطة، هناك العديد من العوامل التي يجب أخذها في الاعتبار عند استخدامها في البرمجة:
-
العشوائية الحقيقية مقابل العشوائية الزائفة: في البرمجة، عادةً ما نستخدم الأعداد العشوائية الزائفة (pseudo-random) التي تولد استنادًا إلى خوارزميات معينة. هذه الأعداد قد تكون “عشوائية” من الناحية العملية ولكنها في الواقع تتبع نمطًا رياضيًا محددًا. في بعض التطبيقات مثل التشفير، قد يكون من الضروري استخدام مولدات أعداد عشوائية حقيقية.
-
البذرة (Seed): إذا كانت الب

