استكشف وحدة random في بايثون. تعرف على العشوائية الزائفة، التمهيد، توليد الأعداد الصحيحة، العشرية، المتسلسلات، وأفضل الممارسات للتطبيقات الآمنة.
وحدة Python Random: تعمق في توليد الأرقام شبه العشوائية
في عالم الحوسبة، العشوائية مفهوم قوي وأساسي. إنها المحرك وراء كل شيء، من المحاكاة العلمية المعقدة ونماذج التعلم الآلي إلى ألعاب الفيديو وتشفير البيانات الآمن. عند العمل مع بايثون، الأداة الأساسية لإدخال هذا العنصر من الصدفة هي وحدة random المدمجة. ومع ذلك، فإن "العشوائية" التي توفرها تأتي مع تحذير حاسم: إنها ليست عشوائية حقًا. إنها شبه عشوائية.
سيأخذك هذا الدليل الشامل في تعمق في وحدة random
في بايثون. سنقوم بإزالة الغموض عن العشوائية الزائفة، واستكشاف وظائف الوحدة الأساسية بأمثلة عملية، والأهم من ذلك، مناقشة متى يجب استخدامها ومتى يجب اللجوء إلى أداة أكثر قوة للتطبيقات الحساسة للأمان. سواء كنت عالم بيانات أو مطور ألعاب أو مهندس برمجيات، فإن الفهم القوي لهذه الوحدة أمر أساسي لمجموعة أدوات بايثون الخاصة بك.
ما هي العشوائية الزائفة؟
قبل أن نبدأ في توليد الأرقام، من الأهمية بمكان فهم طبيعة ما نعمل معه. الكمبيوتر آلة حتمية؛ يتبع التعليمات بدقة. لا يمكنه، بطبيعته، إنتاج رقم عشوائي حقيقي من العدم. يمكن الحصول على العشوائية الحقيقية فقط من الظواهر الفيزيائية غير المتوقعة، مثل الضوضاء الجوية أو الاضمحلال الإشعاعي.
بدلاً من ذلك، تستخدم لغات البرمجة مولدات الأرقام شبه العشوائية (PRNGs). PRNG هو خوارزمية متطورة تنتج سلسلة من الأرقام تبدو عشوائية ولكنها، في الواقع، تحدد بالكامل بواسطة قيمة أولية تسمى التمهيد (seed).
- الخوارزمية الحتمية: يتم إنشاء تسلسل الأرقام بواسطة صيغة رياضية. إذا كنت تعرف الخوارزمية ونقطة البداية، يمكنك التنبؤ بكل رقم في التسلسل.
- التمهيد (Seed): هذا هو الإدخال الأولي للخوارزمية. إذا قدمت نفس التمهيد إلى مولد الأرقام شبه العشوائية (PRNG)، فإنه سينتج نفس التسلسل تمامًا من الأرقام "العشوائية" في كل مرة.
- الدورة (Period): ستتكرر سلسلة الأرقام التي يولدها مولد الأرقام شبه العشوائية (PRNG) في النهاية. بالنسبة لمولد أرقام شبه عشوائية جيد، تكون هذه الدورة كبيرة للغاية، مما يجعلها لا نهائية عملياً لمعظم التطبيقات.
تستخدم وحدة random
في بايثون خوارزمية Mersenne Twister، وهي مولد أرقام شبه عشوائية (PRNG) شائع وقوي للغاية مع فترة طويلة للغاية (219937-1). إنها ممتازة للمحاكاة، أخذ العينات الإحصائية، والألعاب، ولكن كما سنرى لاحقًا، فإن قابليتها للتنبؤ تجعلها غير مناسبة للتشفير.
تمهيد المولد: مفتاح قابلية التكرار
القدرة على التحكم في التسلسل "العشوائي" عبر التمهيد ليست عيبًا؛ إنها ميزة قوية. إنها تضمن قابلية التكرار، وهو أمر ضروري في البحث العلمي، الاختبار، وتصحيح الأخطاء. إذا كنت تجري تجربة تعلم آلي، فأنت بحاجة إلى التأكد من أن تهيئة الأوزان العشوائية أو خلط البيانات هي نفسها في كل مرة لمقارنة النتائج بشكل عادل.
الوظيفة للتحكم في ذلك هي random.seed()
.
دعنا نرى ذلك عملياً. أولاً، لنقم بتشغيل نص برمجي بدون تعيين تمهيد:
import random
print(random.random())
print(random.randint(1, 100))
إذا قمت بتشغيل هذا الكود عدة مرات، ستحصل على نتائج مختلفة في كل مرة. هذا لأنه إذا لم توفر تمهيدًا، فإن بايثون تستخدم تلقائيًا مصدرًا غير حتمي من نظام التشغيل، مثل وقت النظام الحالي، لتهيئة المولد.
الآن، لنقم بتعيين تمهيد:
import random
# Run 1
random.seed(42)
print("Run 1:")
print(random.random()) # Output: 0.6394267984578837
print(random.randint(1, 100)) # Output: 82
# Run 2
random.seed(42)
print("\nRun 2:")
print(random.random()) # Output: 0.6394267984578837
print(random.randint(1, 100)) # Output: 82
كما ترى، من خلال تهيئة المولد بنفس التمهيد (الرقم 42 هو خيار تقليدي، ولكن أي عدد صحيح سيؤدي الغرض)، نحصل على نفس التسلسل الدقيق للأرقام. هذا هو حجر الزاوية في إنشاء محاكاة وتجارب قابلة للتكرار.
توليد الأرقام: الصحيحة والعشرية
توفر وحدة random
مجموعة غنية من الدوال لتوليد أنواع مختلفة من الأرقام.
توليد الأعداد الصحيحة
-
random.randint(a, b)
من المرجح أن تكون هذه هي الدالة الأكثر شيوعًا التي ستستخدمها. تُرجع عددًا صحيحًا عشوائيًا
N
بحيث يكونa <= N <= b
. لاحظ أنها شاملة لكلا النقطتين النهائيتين.# Simulate a standard six-sided die roll die_roll = random.randint(1, 6) print(f"You rolled a {die_roll}")
-
random.randrange(start, stop[, step])
هذه الدالة أكثر مرونة وتتصرف مثل دالة
range()
المدمجة في بايثون. تُرجع عنصرًا مختارًا عشوائيًا منrange(start, stop, step)
. والأهم من ذلك، أنها غير شاملة لقيمةstop
.# Get a random even number between 0 and 10 (exclusive of 10) even_number = random.randrange(0, 10, 2) # Possible outputs: 0, 2, 4, 6, 8 print(f"A random even number: {even_number}") # Get a random number from 0 to 99 num = random.randrange(100) # Equivalent to random.randrange(0, 100, 1) print(f"A random number from 0-99: {num}")
توليد الأرقام العشرية
-
random.random()
هذه هي الدالة الأساسية لتوليد الأعداد العشرية. تُرجع عددًا عشريًا عشوائيًا في النطاق نصف المفتوح
[0.0, 1.0)
. هذا يعني أنها يمكن أن تشمل 0.0 ولكنها ستكون دائمًا أقل من 1.0.# Generate a random float between 0.0 and 1.0 probability = random.random() print(f"Generated probability: {probability}")
-
random.uniform(a, b)
للحصول على عدد عشري عشوائي ضمن نطاق محدد، استخدم
uniform()
. تُرجع عددًا عشريًا عشوائيًاN
بحيث يكونa <= N <= b
أوb <= N <= a
.# Generate a random temperature in Celsius for a simulation temp = random.uniform(15.5, 30.5) print(f"Simulated temperature: {temp:.2f}°C")
-
توزيعات أخرى
تدعم الوحدة أيضًا توزيعات أخرى متنوعة تحاكي ظواهر العالم الحقيقي، وهي لا تقدر بثمن للمحاكاة المتخصصة:
random.gauss(mu, sigma)
: التوزيع الطبيعي (أو الغاوسي)، مفيد لنمذجة أشياء مثل أخطاء القياس أو درجات الذكاء.random.expovariate(lambd)
: التوزيع الأسي، غالبًا ما يستخدم لنمذجة الوقت بين الأحداث في عملية بواسون.random.triangular(low, high, mode)
: التوزيع المثلثي، مفيد عندما يكون لديك قيمة دنيا، قصوى، وقيمة أكثر احتمالًا.
العمل مع المتسلسلات
غالبًا، لا تحتاج فقط إلى رقم عشوائي؛ بل تحتاج إلى إجراء اختيار عشوائي من مجموعة من العناصر أو إعادة ترتيب قائمة عشوائيًا. تتفوق وحدة random
في هذا.
الاختيارات والتحديدات
-
random.choice(seq)
تُرجع هذه الدالة عنصرًا واحدًا، يتم اختياره عشوائيًا، من متسلسلة غير فارغة (مثل قائمة، tuple، أو سلسلة نصية). إنها بسيطة وفعالة للغاية.
participants = ["Alice", "Bob", "Charlie", "David", "Eve"] winner = random.choice(participants) print(f"And the winner is... {winner}!") possible_moves = ("rock", "paper", "scissors") computer_move = random.choice(possible_moves) print(f"Computer chose: {computer_move}")
-
random.choices(population, weights=None, k=1)
للسيناريوهات الأكثر تعقيدًا، تسمح لك دالة
choices()
(بصيغة الجمع) باختيار عناصر متعددة من مجموعة، مع الإرجاع. وهذا يعني أنه يمكن اختيار نفس العنصر أكثر من مرة. يمكنك أيضًا تحديد قائمة بـweights
لجعل بعض الخيارات أكثر احتمالًا من غيرها.# Simulate 10 coin flips flips = random.choices(["Heads", "Tails"], k=10) print(flips) # Simulate a weighted dice roll where 6 is three times more likely outcomes = [1, 2, 3, 4, 5, 6] weights = [1, 1, 1, 1, 1, 3] weighted_roll = random.choices(outcomes, weights=weights, k=1)[0] print(f"Weighted roll result: {weighted_roll}")
-
random.sample(population, k)
عندما تحتاج إلى اختيار عناصر فريدة متعددة من مجموعة، استخدم
sample()
. إنها تقوم باختيار بدون إرجاع. هذا مثالي لسيناريوهات مثل سحب أرقام اليانصيب أو اختيار فريق مشروع عشوائي.# Select 3 unique numbers for a lottery draw from 1 to 50 lottery_numbers = range(1, 51) winning_numbers = random.sample(lottery_numbers, k=3) print(f"The winning numbers are: {winning_numbers}") # Form a random team of 2 from the participant list team = random.sample(participants, k=2) print(f"The new project team is: {team}")
خلط متسلسلة
-
random.shuffle(x)
تُستخدم هذه الدالة لإعادة ترتيب العناصر عشوائيًا في متسلسلة قابلة للتغيير (مثل قائمة). من المهم تذكر أن
shuffle()
تعدل القائمة في مكانها وتُرجعNone
. لا ترتكب الخطأ الشائع المتمثل في تعيين قيمتها المرجعة لمتغير.# Shuffle a deck of cards cards = ["Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"] print(f"Original order: {cards}") random.shuffle(cards) print(f"Shuffled order: {cards}") # Incorrect usage: # shuffled_cards = random.shuffle(cards) # This will set shuffled_cards to None!
تحذير حاسم: لا تستخدم \`random\` للتشفير أو الأمان
هذه هي أهم نقطة يجب أخذها في الاعتبار لأي مطور محترف. إن قابلية التنبؤ بمولد الأرقام شبه العشوائية (PRNG) من نوع Mersenne Twister تجعله غير آمن تمامًا لأي غرض يتعلق بالأمان. إذا تمكن مهاجم من ملاحظة بعض الأرقام من التسلسل، يمكنه نظريًا حساب التمهيد والتنبؤ بجميع الأرقام "العشوائية" اللاحقة.
لا تستخدم وحدةrandom
أبدًا من أجل:
- توليد كلمات المرور، رموز الجلسة، أو مفاتيح API.
- إنشاء "salt" لتجزئة كلمات المرور.
- أي وظيفة تشفير مثل توليد مفاتيح التشفير.
- آليات إعادة تعيين كلمة المرور.
الأداة المناسبة للمهمة: وحدة \`secrets\`
للتطبيقات الحساسة للأمان، توفر بايثون وحدة secrets
(المتاحة منذ بايثون 3.6). تم تصميم هذه الوحدة خصيصًا لاستخدام أكثر مصادر العشوائية أمانًا التي يوفرها نظام التشغيل. يُشار إلى هذا غالبًا باسم مولد الأرقام شبه العشوائية الآمن تشفيريًا (CSPRNG).
إليك كيفية استخدامها لمهام الأمان الشائعة:
import secrets
import string
# Generate a secure, 16-byte token in hexadecimal format
api_key = secrets.token_hex(16)
print(f"Secure API Key: {api_key}")
# Generate a secure URL-safe token
password_reset_token = secrets.token_urlsafe(32)
print(f"Password Reset Token: {password_reset_token}")
# Generate a strong, random password
# This creates a password with at least one lowercase, one uppercase, and one digit
-alphabet = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alphabet) for i in range(12))
print(f"Generated Password: {password}")
القاعدة بسيطة: إذا كان الأمر يتعلق بالأمان، استخدم secrets
. إذا كان للنمذجة أو الإحصائيات أو الألعاب، فإن random
هو الخيار الصحيح.
للحوسبة عالية الأداء: \`numpy.random\`
بينما تعتبر وحدة random
القياسية ممتازة للمهام ذات الأغراض العامة، إلا أنها ليست محسّنة لتوليد مصفوفات كبيرة من الأرقام، وهو متطلب شائع في علم البيانات، التعلم الآلي، والحوسبة العلمية. لهذه التطبيقات، تعد مكتبة NumPy هي المعيار الصناعي.
تعد وحدة numpy.random
أكثر كفاءة بشكل ملحوظ لأن تطبيقها الأساسي مكتوب بلغة C المترجمة. كما أنها مصممة للعمل بسلاسة مع كائنات المصفوفات القوية في NumPy.
لنقارن بناء الجملة لتوليد مليون عدد عشري عشوائي:
import random
import numpy as np
import time
# Using the standard library \`random\`
start_time = time.time()
random_list = [random.random() for _ in range(1_000_000)]
end_time = time.time()
print(f"Standard 'random' took: {end_time - start_time:.4f} seconds")
# Using NumPy
start_time = time.time()
numpy_array = np.random.rand(1_000_000)
end_time = time.time()
print(f"NumPy 'numpy.random' took: {end_time - start_time:.4f} seconds")
ستجد أن NumPy أسرع بعدة درجات من حيث الحجم. كما أنها توفر مجموعة أوسع بكثير من التوزيعات الإحصائية والأدوات للعمل مع البيانات متعددة الأبعاد.
أفضل الممارسات وأفكار أخيرة
دعنا نلخص رحلتنا مع بعض أفضل الممارسات الرئيسية:
- التمهيد من أجل قابلية التكرار: استخدم دائمًا
random.seed()
عندما تحتاج إلى أن تكون عملياتك العشوائية قابلة للتكرار، كما هو الحال في الاختبارات، المحاكاة، أو تجارب التعلم الآلي. - الأمان أولاً: لا تستخدم أبدًا وحدة
random
لأي شيء يتعلق بالأمان أو التشفير. استخدم دائمًا وحدةsecrets
بدلاً من ذلك. هذا غير قابل للتفاوض. - اختر الدالة الصحيحة: استخدم الدالة التي تعبر عن قصدك بشكل أفضل. هل تحتاج إلى اختيار فريد؟ استخدم
random.sample()
. هل تحتاج إلى اختيار مرجح مع الإرجاع؟ استخدمrandom.choices()
. - الأداء مهم: للعمليات الحسابية الثقيلة، خاصة مع مجموعات البيانات الكبيرة، استغل قوة وسرعة
numpy.random
. - فهم العمليات الموقعية (In-Place): كن حذرًا من أن
random.shuffle()
تعدل القائمة في مكانها.
الخاتمة
تعد وحدة random
في بايثون جزءًا متعدد الاستخدامات ولا غنى عنه في المكتبة القياسية. من خلال فهم طبيعتها شبه العشوائية وإتقان وظائفها الأساسية لتوليد الأرقام والعمل مع المتسلسلات، يمكنك إضافة طبقة قوية من السلوك الديناميكي إلى تطبيقاتك. والأهم من ذلك، بمعرفة قيودها ومتى يجب اللجوء إلى أدوات متخصصة مثل secrets
أو numpy.random
، فإنك تظهر بعد النظر والاجتهاد لمهندس برمجيات محترف. لذا انطلق – قم بالمحاكاة، والخلط، والاختيار بثقة!