System.Random sınıfı sözde rastgele sayılar üretir: bir seed'den başlayan, rastgele görünen belirlenimci bir dizi. Bir örnek oluşturun ve metotlarını çağırın:
Örnek çıktı:
1294270287
83
You rolled a 5
0.6060
Tekrar çalıştırın, sayılar değişir. Üst sınır dahil değildir: Next(1, 7) 1, 2, 3, 4, 5 ya da 6 döndürebilir, asla 7 döndürmez. 1 ile 10 arası (10 dahil) bir sayı için Next(1, 11) yazın. min'den küçük bir max vermek ArgumentOutOfRangeException fırlatır.
Bir aralıkta rastgele sayılar
Next(min, max) tamsayıları karşılar. Bir aralıktaki double için NextDouble()'ı ölçekleyin; fiyat gibi bir decimal için sonucu istediğiniz hassasiyette olacak şekilde tamsayılardan oluşturun:
Örnek çıktı:
23.6 C
72.42
heads
N
-3
rng.Next(100, 10000) / 100m tam sayıda kuruş seçer ve bir decimal'e böler, böylece fiyatın her zaman tam iki ondalığı olur. Bir double'ı ölçeklemek 72.4183 gibi değerler verirdi.
Seed'ler: tekrarlanabilir diziler
Bir tamsayı seed vermek diziyi tekrarlanabilir yapar. Aynı seed'e sahip iki Random nesnesi tam olarak aynı sayıları aynı sırada üretir:
Çıktı:
16 29 91 99 91
16 29 91 99 91
38
Seed'ler testler, yeniden çalıştırmanız gereken simülasyonlar, oyunlardaki tekrar oynatmalar ve prosedürel olarak üretilen seviyeler ("dünya seed'i 2026") için istediğiniz şeydir. Kullandığınız seed'i log'a yazın, şaşırtıcı bir sonuç daha sonra yeniden üretilebilir.
Microsoft, bir seed'in her .NET sürümünde aynı diziyi üreteceğini garanti etmez. Seed'li çıktıyı veri olarak kaydetmeyin (örneğin sabit bir seed'den müşteri ID'leri üretip bir yükseltmeden sonra onları yeniden üretmeyi beklemek).
Rastgele bir eleman seçmek
Rastgele bir indeks Next(0, count)'tur; üst sınır dahil olmadığı için her zaman geçerli bir indekstir:
Çıktı:
Winner 1 gets a sticker pack
Winner 2 gets a mug
Winner 3 gets a sticker pack
common 695
rare 260
legendary 45
Ağırlıklı çekiliş, 0 ile 99 arası bir sayıyı boyutları ağırlıklar olan aralıklara eşler: 0 ile 69 arası common, 70 ile 94 arası rare, 95 ile 99 arası legendary. 1.000 çekilişte sayılar 700, 250 ve 50'ye yakın çıkar (burada 695, 260 ve 45).
Bir listeyi karıştırmak: Fisher-Yates
Bir listeyi rastgele sıraya koymak için sondan başlayarak ilerleyin ve her elemanı kendisinde ya da öncesinde bulunan rastgele bir elemanla takas edin. Bu Fisher-Yates karıştırmasıdır; hızlıdır ve her sıralama eşit olasılıklıdır. Generic bir metot olarak yazıldığında her eleman tipindeki diziler ve listeler için çalışır:
Çıktı:
5 A 8 6 4 7 2 3
4 2 1 3 5
İnternette göreceğiniz iki kısayol daha kötüdür. list.OrderBy(x => rng.Next()) çalışır ama sıralama yapar, bu yüzden daha yavaştır ve rastgele anahtarlar çakışabilir. rng.Next(i + 1) yerine rng.Next(items.Count) ile takas etmek benzer görünür ama bazı sıraları diğerlerinden daha olası kılar. .NET 8 ve sonrasında Random.Shared.Shuffle(array) yerinde doğru bir karıştırma yapar ve rng.GetItems(choices, 5) beş rastgele öğe seçer (tekrarlı).
Döngüde new Random() tuzağı
Klasik bir hata, her sayı gerektiğinde yeni bir Random oluşturmaktır:
// Don't do this
int RollDie()
{
var rng = new Random(); // new instance on every call
return rng.Next(1, 7);
}
.NET Framework'te seed'siz new Random() sistem saatini (Environment.TickCount) kullanır; bu yalnızca 10 ile 16 milisaniyede bir değişir. Hızlı bir döngüde yapılan çağrılar aynı seed'e sahip örnekler oluşturur ve aynı sayıyı döndürür, böylece bir zar atma döngüsü 4 4 4 4 4 yazdırır. .NET Core ve .NET 5+ her örneğe paylaşılan rastgele bir kaynaktan seed verir, bu da belirtiyi gizler; ama sayı başına bir nesne oluşturmak yine de israftır ve kod eski projelerde ya da Unity'de yeniden kullanıldığında bozulur.
Çözüm, bir alanda saklanan, sınıf için tek bir örnektir:
Çıktı:
3 6 6 5 6 1 2 1
Buradaki seed yalnızca örneğin her çalıştırmada aynı şeyi yazdırması içindir; gerçek bir oyunda new Random() yazardınız.
Thread'ler ve Random.Shared
Random thread-safe değildir. İki thread aynı örnekte aynı anda Next çağırırsa iç durumu bozulabilir; bilinen belirtisi, o andan itibaren 0 döndüren bir örnektir. En basitinden başlayarak seçenekler:
static class Dice
{
// .NET 6 and later: a thread-safe shared instance
public static int RollShared() => Random.Shared.Next(1, 7);
// Any version: one instance per thread
[ThreadStatic] private static Random _local;
private static Random Local => _local ?? (_local = new Random(Guid.NewGuid().GetHashCode()));
public static int RollPerThread() => Local.Next(1, 7);
// Any version: a lock around one shared instance
private static readonly Random _rng = new Random();
private static readonly object _sync = new object();
public static int RollLocked()
{
lock (_sync) { return _rng.Next(1, 7); }
}
}
Yeni kodda doğru varsayılan Random.Shared'dir: thread-safe'tir, alan gerektirmez ve rastgele seed alır. Seed verilemez, bu yüzden tekrarlanabilirlik gerektiğinde kendi new Random(seed)'inizi kullanın.
Kriptografik olarak güvenli rastgele sayılar
System.Random tasarım gereği öngörülebilirdir. Seed'i bilen ya da yeterince çıktı gören herkes gerisini hesaplayabilir. Bu oyunlar ve örnekleme için sorun değildir; parolalar, sıfırlama token'ları, oturum ID'leri, anahtarlar ve çekilişler için yanlıştır. Bunlar için işletim sisteminin güvenli üretecinden okuyan System.Security.Cryptography içindeki RandomNumberGenerator'ı kullanın:
Örnek çıktı:
Verification code: 276062
Reset token: 391e40ab5fa205e6457e48661586d10a
Password: gyv8LWyajJcF
RandomNumberGenerator.GetInt32 (.NET Core 3.0 ve sonrasında vardır) aralıkta yansız bir tamsayı döndürür. Güvenli kodları bytes[0] % 10 ile oluşturmayın: mod işlemi bazı rakamları diğerlerinden daha olası kılar. .NET 6 ve sonrasında RandomNumberGenerator.GetBytes(16) doğrudan yeni bir dizi döndürür ve Convert.ToHexString(bytes) onu biçimlendirir. Gizliliğin önemli olmadığı benzersiz tanımlayıcılar için Guid.NewGuid() daha basittir.
Sıkça Sorulan Sorular
C#'ta rastgele sayı nasıl üretilir?
Bir Random nesnesi oluşturun ve Next çağırın: var rng = new Random(); int roll = rng.Next(1, 7); 1 ile 6 arası verir. Next(max) 0 ile max - 1 arası verir, NextDouble() ise 0.0'dan 1.0'a kadar (1.0 hariç) bir double verir. .NET 6 ve sonrasında nesneyi atlayıp Random.Shared.Next(1, 7) kullanabilirsiniz.
Random.Next'in üst sınırı dahil mi?
Hayır. Next(min, max), min'den büyük ya da eşit ve max'tan kesinlikle küçük bir sayı döndürür. 1 ile 10 arası (10 dahil) bir sayı için Next(1, 11) yazın. Bu, Next(0, list.Count)'u bir listeye geçerli bir indeks yapar.
new Random() neden aynı sayıları veriyor?
.NET Framework'te new Random() sistem saatinden seed alır; saat yalnızca birkaç milisaniyede bir değişir, bu yüzden hızlı bir döngüde oluşturulan birkaç örnek aynı seed'i ve aynı diziyi alır. .NET Core ve .NET 5+ her örneğe farklı seed verir, ama çözüm her yerde aynıdır: bir Random oluşturun ve onu yeniden kullanın.
C#'ta her seferinde aynı rastgele sayılar nasıl alınır?
Constructor'a bir seed verin: new Random(42). Aynı seed'e sahip iki örnek aynı diziyi üretir; bu da testleri, simülasyonları ve prosedürel üretimi tekrarlanabilir kılar. Belirli bir seed'in dizisi .NET sürümleri arasında farklı olabilir, bu yüzden seed'li çıktıyı kalıcı veri olarak saklamayın.
System.Random parolalar ya da token'lar için yeterince güvenli mi?
Hayır. Random öngörülebilirdir: durumunu ya da seed'ini öğrenen herkes sayıları yeniden üretebilir. Parolalar, token'lar, anahtarlar ve güvenlikle ilgili her şey için System.Security.Cryptography.RandomNumberGenerator kullanın, örneğin RandomNumberGenerator.GetInt32(0, 10) ya da RandomNumberGenerator.GetBytes(32).