ماژول تصادفی پایتون را کاوش کنید. در مورد شبه تصادفی بودن، مقداردهی اولیه، تولید اعداد صحیح، اعشاری، دنبالهها و بهترین روشها برای برنامههای امنیتی بیاموزید.
ماژول تصادفی پایتون: غواصی عمیق در تولید اعداد شبه تصادفی
در دنیای محاسبات، تصادفی بودن یک مفهوم قدرتمند و ضروری است. این موتور محرکه همه چیز از شبیهسازیهای علمی پیچیده و مدلهای یادگیری ماشینی گرفته تا بازیهای ویدیویی و رمزگذاری امن دادهها است. هنگام کار با پایتون، ابزار اصلی برای معرفی این عنصر شانس، ماژول داخلی تصادفی است. با این حال، «تصادفی بودن»ی که ارائه میدهد، یک هشدار مهم دارد: واقعاً تصادفی نیست. این شبه تصادفی است.
این راهنمای جامع شما را به یک غواصی عمیق در ماژول random
پایتون میبرد. ما شبه تصادفی بودن را رمزگشایی میکنیم، عملکردهای اصلی ماژول را با مثالهای عملی بررسی میکنیم و، مهمتر از همه، بحث خواهیم کرد که چه زمانی از آن استفاده کنیم و چه زمانی برای برنامههای حساس به امنیت به دنبال یک ابزار قویتر باشیم. چه دانشمند داده، چه توسعهدهنده بازی یا مهندس نرمافزار باشید، درک جامد این ماژول برای ابزار پایتون شما اساسی است.
شبه تصادفی بودن چیست؟
قبل از اینکه شروع به تولید اعداد کنیم، درک ماهیت آنچه روی آن کار میکنیم بسیار مهم است. یک کامپیوتر یک ماشین قطعی است؛ دستورالعملها را دقیقاً دنبال میکند. به خودی خود، نمیتواند یک عدد واقعاً تصادفی را از هیچ تولید کند. تصادفی بودن واقعی فقط میتواند از پدیدههای فیزیکی غیرقابل پیشبینی، مانند نویز اتمسفر یا واپاشی رادیواکتیو، سرچشمه بگیرد.
در عوض، زبانهای برنامهنویسی از تولیدکنندههای اعداد شبه تصادفی (PRNGs) استفاده میکنند. یک PRNG یک الگوریتم پیچیده است که یک دنباله از اعداد تولید میکند که تصادفی به نظر میرسد اما در واقع کاملاً توسط یک مقدار اولیه به نام seed تعیین میشود.
- الگوریتم قطعی: دنباله اعداد توسط یک فرمول ریاضی تولید میشود. اگر الگوریتم و نقطه شروع را بدانید، میتوانید هر عدد را در دنباله پیشبینی کنید.
- Seed: این ورودی اولیه الگوریتم است. اگر یک seed یکسان را به PRNG ارائه دهید، دقیقاً همان دنباله اعداد «تصادفی» را هر بار تولید میکند.
- دوره تناوب: دنباله اعداد تولید شده توسط یک PRNG در نهایت تکرار میشود. برای یک PRNG خوب، این دوره نجومی بزرگ است و آن را برای اکثر برنامهها عملاً نامحدود میکند.
ماژول random
پایتون از الگوریتم Mersenne Twister استفاده میکند، یک PRNG بسیار محبوب و قوی با یک دوره فوقالعاده طولانی (219937-1). برای شبیهسازی، نمونهبرداری آماری و بازی عالی است، اما همانطور که بعداً خواهیم دید، قابلیت پیشبینی آن، آن را برای رمزنگاری نامناسب میکند.
مقداردهی اولیه تولیدکننده: کلید تولید مثل
توانایی کنترل دنباله «تصادفی» از طریق seed یک نقص نیست. این یک ویژگی قدرتمند است. قابلیت تکثیر را تضمین میکند، که در تحقیقات علمی، آزمایش و اشکالزدایی ضروری است. اگر در حال اجرای یک آزمایش یادگیری ماشینی هستید، باید اطمینان حاصل کنید که مقداردهی اولیه وزن تصادفی یا به هم زدن دادههای شما در هر زمان یکسان باشد تا نتایج را منصفانه مقایسه کنید.
تابع کنترل این مقدار random.seed()
است.
بیایید آن را در عمل ببینیم. ابتدا، بیایید یک اسکریپت را بدون تنظیم seed اجرا کنیم:
import random
print(random.random())
print(random.randint(1, 100))
اگر این کد را چندین بار اجرا کنید، هر بار نتایج متفاوتی دریافت خواهید کرد. دلیل این است که اگر یک seed ارائه نکنید، پایتون به طور خودکار از یک منبع غیرقطعی از سیستم عامل، مانند زمان سیستم فعلی، برای مقداردهی اولیه تولیدکننده استفاده میکند.
حالا، بیایید یک seed تنظیم کنیم:
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("
Run 2:")
print(random.random()) # Output: 0.6394267984578837
print(random.randint(1, 100)) # Output: 82
همانطور که میبینید، با مقداردهی اولیه تولیدکننده با یک seed یکسان (عدد 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)
: توزیع نرمال (یا گاوسی)، مفید برای مدلسازی چیزهایی مانند خطاهای اندازهگیری یا نمرات IQ.random.expovariate(lambd)
: توزیع نمایی، که اغلب برای مدلسازی زمان بین رویدادها در یک فرآیند پواسون استفاده میشود.random.triangular(low, high, mode)
: توزیع مثلثی، زمانی که حداقل، حداکثر و محتملترین مقدار را دارید مفید است.
کار با دنبالهها
اغلب، شما فقط به یک عدد تصادفی نیاز ندارید؛ شما باید یک انتخاب تصادفی از مجموعهای از موارد یا مرتبسازی مجدد یک لیست به صورت تصادفی انجام دهید. ماژول random
در این زمینه عالی است.
انتخابها و انتخابها
-
random.choice(seq)
این تابع یک عنصر واحد و انتخاب شده به صورت تصادفی را از یک دنباله غیر خالی (مانند لیست، تاپل یا رشته) برمیگرداند. ساده و بسیار موثر است.
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 آن را برای هر هدف مرتبط با امنیت کاملاً ناامن میکند. اگر یک مهاجم بتواند چند عدد از دنباله را مشاهده کند، میتواند seed را محاسبه کرده و تمام اعداد «تصادفی» بعدی را پیشبینی کند.
هرگز از ماژول random
برای موارد زیر استفاده نکنید:
- تولید گذرواژهها، توکنهای جلسه یا کلیدهای API.
- ایجاد نمک برای هش کردن رمز عبور.
- هر عملکرد رمزنگاری مانند تولید کلیدهای رمزگذاری.
- مکانیسمهای بازنشانی رمز عبور.
ابزار مناسب برای کار: ماژول 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 چندین برابر سریعتر است. همچنین مجموعه وسیعتری از توزیعهای آماری و ابزارهایی برای کار با دادههای چند بعدی ارائه میدهد.
بهترین شیوهها و افکار نهایی
بیایید سفر خود را با برخی از بهترین شیوههای کلیدی خلاصه کنیم:
- Seed برای تولید مثل: همیشه از
random.seed()
استفاده کنید، هنگامی که نیاز دارید فرآیندهای تصادفی شما قابل تکرار باشند، مانند آزمایشها، شبیهسازیها یا آزمایشهای یادگیری ماشینی. - امنیت اول: هرگز از ماژول
random
برای هیچ چیزی مرتبط با امنیت یا رمزنگاری استفاده نکنید. همیشه به جای آن از ماژولsecrets
استفاده کنید. این غیرقابل مذاکره است. - تابع مناسب را انتخاب کنید: از تابعی استفاده کنید که بهترین بیان را از هدف شما دارد. نیاز به انتخاب منحصر به فرد دارید؟ از
random.sample()
استفاده کنید. نیاز به انتخاب وزنی با جایگزینی دارید؟ ازrandom.choices()
استفاده کنید. - عملکرد مهم است: برای بالابردن عددی سنگین، بهویژه با مجموعهدادههای بزرگ، از قدرت و سرعت
numpy.random
استفاده کنید. - عملکردها درجا را درک کنید: توجه داشته باشید که
random.shuffle()
یک لیست را درجا اصلاح میکند.
نتیجهگیری
ماژول random
پایتون بخش همهکاره و ضروری از کتابخانه استاندارد است. با درک ماهیت شبه تصادفی آن و تسلط بر عملکردهای اصلی آن برای تولید اعداد و کار با دنبالهها، میتوانید یک لایه قدرتمند از رفتار پویا را به برنامههای خود اضافه کنید. مهمتر از آن، با دانستن محدودیتهای آن و زمانی که به دنبال ابزارهای تخصصی مانند secrets
یا numpy.random
هستید، پیشبینی و پشتکار یک مهندس نرمافزار حرفهای را نشان میدهید. پس ادامه دهید—با اطمینان شبیهسازی کنید، به هم بزنید و انتخاب کنید!