Aynı soru bende de zamanında kafayı yedirmişti, çünkü "rastgele" deyince insan gerçekten her şeyin tamamen kaotik olmasını bekliyor ama işler biraz farklı yürüyor.
Şimdi Python’da rastgele sayı üretirken aslında tam anlamıyla “gerçek” rastgelelikten bahsetmiyoruz. Python’un kullandığı rastgele sayı üreticisi, matematiksel bir algoritma — yani olaylar tamamen yazılım temelli, fiziksel değil. Bu tür algoritmalara psödo-rastgele sayı üreteci (İngilizcesi: pseudo-random number generator, kısaca PRNG) deniyor. Yani “mış gibi” rastgele. Gerçek değil.
Python’da bu işin kalbi, random modülündeki Mersenne Twister algoritması. Bu algoritma oldukça iyi, hatta çoğu iş için gayet yeterli ama mükemmel değil. Bazen dağılımda küçük dengesizlikler göze çarpabiliyor. Hele senin gibi histogram bakıp "yahu bu neden böyle?" diyorsan, orada algoritmanın limitlerini görüyorsun demektir.
Bir örnek vereyim, mesela şu kodu düşün:
import random
import matplotlib.pyplot as plt
numbers = [random.randint(4, 10) for _ in range(2000)]
# Sayıları say
count_dict = {i: numbers.count(i) for i in range(4, 11)}
print("Sayıların dağılımı:")
for num, count in count_dict.items():
print(f"{num}: {count} kez")
# Histogram çiz
plt.hist(numbers, bins=range(4, 12), edgecolor='black', align='left', rwidth=0.8)
plt.title("Python random.randint ile üretilen sayılar (4-10)")
plt.xlabel("Sayı")
plt.ylabel("Frekans")
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.show()
Bu kodda 2000 sayı üretip hem ekrana kaç kere geldiklerini yazdırıyoruz hem de histogramla görselleştiriyoruz.
Bu sayıları alıp histogram yapınca teoride 4 ile 10 arasında (yani 7 tane sayı) eşit sayıda dağılmış olmasını bekliyoruz, değil mi? Her sayıdan yaklaşık 2000 / 7 ≈ 285 civarında olmalı. Ama işte bazen 260 çıkıyor, bazen 310. Bu ufak oynama, algoritmanın doğasından kaynaklı. Üstelik randint fonksiyonunun nasıl sayıları yuvarladığı, hangi aralıkta nasıl ağırlık verdiği gibi konular da işin içine giriyor.
Üniform dağılım testi de burada devreye giriyor. Bu test aslında "her sayı eşit olasılıkla geliyor mu?" diye bakıyor. Eğer test başarısız olduysa, muhtemelen ya örnek sayısı az geldi ya da algoritma o aralıkta gerçekten pek düzgün çalışmadı. Ama bak, burada şu da önemli: 2000 sayı bazen istatistiksel olarak yeterli olmayabilir. Tesadüfen dengesizlik olabilir. 2000 küçük bir örnek değil ama yine de, "gürültü" olabilir. O yüzden bazen bu testler çok daha büyük sayılarda yapılır.
Ama diyelim ki senin testin çok sağlam ve hakikaten düzgün çalışmadıysa... Bunun nedeni birkaç şey olabilir:
- Mersenne Twister algoritması bazı aralıklarda küçük sapmalar gösterebilir.
- randint fonksiyonu altında yatan hesaplama ufak bias (sapma) yaratıyor olabilir.
- NumPy gibi kütüphanelerde bile bazen rastgelelik daha iyi optimize edilir, Python’un random modülü biraz daha basit kalabilir.
Peki çözüm? Yani “daha iyi nasıl rastgele sayı elde ederim” diyorsan...
İki yol var:
Eğer kripto seviyesinde daha “gerçek” rastgelelik istersen, Python’da secrets modülü var. Bu modül daha güçlü, özellikle güvenlik amaçlı kullanılıyor. Ama biraz yavaş.
Bu modül normalde güvenlik işleri için ama dağılım açısından yine iş görür. Fakat biraz daha “yavaş çalışır.” Üretim süresi fark edilebilir.
import secrets
import matplotlib.pyplot as plt
from collections import Counter
numbers = [secrets.randbelow(7) + 4 for _ in range(2000)]
count_dict = Counter(numbers)
print("secrets modülüyle üretilen sayıların dağılımı:")
for num in range(4, 11):
print(f"{num}: {count_dict[num]} kez")
plt.hist(numbers, bins=range(4, 12), edgecolor='black', align='left', rwidth=0.8, color='green')
plt.title("secrets.randbelow ile üretilen sayılar (4-10)")
plt.xlabel("Sayı")
plt.ylabel("Frekans")
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.show()
- Eğer çok büyük ölçekli rastgelelik gerekiyorsa, fiziksel rastgelelik devreye giriyor. Mesela bazı sistemler ortam gürültüsünden, CPU’nun sıcaklık değişiminden ya da kuantum olaylardan veri çekerek rastgelelik üretir. Bu iş artık çok ileri seviye. Ama Python'da buna yaklaşmak için os.urandom gibi şeyler var.
- Daha kontrollü ve bilimsel işler için NumPy önerilir. Çünkü NumPy’in random modülü (numpy.random) biraz daha optimize çalışır. Üstelik dağılımları da test etmek kolaydır.
import numpy as np
import matplotlib.pyplot as plt
numbers = np.random.randint(4, 11, size=2000)
unique, counts = np.unique(numbers, return_counts=True)
print("NumPy ile üretilen sayıların dağılımı:")
for num, count in zip(unique, counts):
print(f"{num}: {count} kez")
plt.hist(numbers, bins=range(4, 12), edgecolor='black', align='left', rwidth=0.8, color='orange')
plt.title("NumPy randint ile üretilen sayılar (4-10)")
plt.xlabel("Sayı")
plt.ylabel("Frekans")
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.show()
Burada genelde dağılım daha düzgün olur. Sayılar arasında fark olabilir ama “göze batar” bir dengesizlik nadiren çıkar. NumPy bu işi biraz daha “ince işçilik” gibi yapıyor.
Sonuç olarak, Python’daki rastgelelik "iyi ama mükemmel değil." Çoğu iş için yeterli ama senin gibi detaylı bakan gözler varsa, bazı şeyler batabilir. O zaman da ya örnek sayısını büyütmek gerek ya da daha kaliteli algoritmalar kullanmak gerek.
Ve evet, rastgelelik işinde biraz tuhaflık hissediyorsan, yalnız değilsin. Çok kişi o histogramları görünce "bu niye böyle?" diye takılıyor. Gerçek rastgelelik bazen düzensiz görünüp daha rastgeledir, bazen de düzenli bir şey görünce şüpheleniriz ama o rastgele olabilir. İşte bu kafa karışıklığı olayın en tatlı kısmı. :)
Kullanılan sayıların rastgelelik durumlarını analiz edebilmek için Chi-squared testini de kullanabilirsin. Grafikleri yorumlamanı kolaylaştıracaktır.