Sadece bir rastgele sayı üreten rastgele sayı üreteci


765

Ben aşağıdaki işlevi var:

//Function to get random number
public static int RandomNumber(int min, int max)
{
    Random random = new Random();
    return random.Next(min, max);
}

Nasıl diyorum:

byte[] mac = new byte[6];
for (int x = 0; x < 6; ++x)
    mac[x] = (byte)(Misc.RandomNumber((int)0xFFFF, (int)0xFFFFFF) % 256);

Ben çalışma sırasında hata ayıklayıcı ile bu döngü adım (Ben istediğim budur) farklı değerler olsun. Ancak, bu kodun altına iki satır kesme noktası koyarsam, macdizinin tüm üyeleri eşit değere sahiptir.

Bu neden oluyor?


20
kullanarak new Random().Next((int)0xFFFF, (int)0xFFFFFF) % 256);daha iyi "rastgele" sayılar .Next(0, 256)
vermez

Bu NuGet paketini faydalı bulabilirsiniz . Rand.Next(int, int)Tohum yeniden kullanım problemine kilitlenmeden veya çalışmadan rastgele değerlere statik erişim sağlayan statik bir yöntem sağlar
ChaseMedallion

Yanıtlar:


1042

Her yaptığınızda new Random()saat kullanılarak başlatılır. Bu, sıkı bir döngüde aynı değeri birçok kez elde ettiğiniz anlamına gelir. Tek bir tutmalı Rastgele örneği ve kullanmaya devam İleri üzerinde aynı örneği.

//Function to get a random number 
private static readonly Random random = new Random(); 
private static readonly object syncLock = new object(); 
public static int RandomNumber(int min, int max)
{
    lock(syncLock) { // synchronize
        return random.Next(min, max);
    }
}

Düzenleme (yorumlara bakın): neden lockburaya ihtiyacımız var?

Temel olarak, örneğin Nextiç durumunu değiştirecektir Random. Biz birden çok iş parçacığı aynı anda o yaparsanız olabilir iddia "Biz sadece sonuç daha rastgele yaptık" ama ne biz edilir aslında potansiyel olarak iç uygulanmasını kırılıyor yapıyor ve biz de aynı numaraları almak başlayabileceğini farklı iplikler, gelen olabilir ve olmayabilir - bir sorun. Bununla birlikte, dahili olarak ne olacağının garantisi daha büyük bir sorundur; çünkü Randomyok değil parçacığı güvenliği hiçbir garanti. Dolayısıyla iki geçerli yaklaşım vardır:

  • Farklı iş parçacıklarından aynı anda erişmemiz için senkronize edin
  • RandomHer iş parçacığı için farklı örnekler kullanın

Her ikisi de iyi olabilir; ancak aynı anda birden çok arayandan gelen tek bir örneği kesmek , sadece sorun istemektir.

lockBu yaklaşımların birinci (ve daha basit) ulaşır; ancak, başka bir yaklaşım şöyle olabilir:

private static readonly ThreadLocal<Random> appRandom
     = new ThreadLocal<Random>(() => new Random());

bu iş parçacığı başına yapılır, bu nedenle senkronize etmeniz gerekmez.


19
Genel bir kural olarak, tüm statik yöntemler iş parçacığı için güvenli hale getirilmelidir, çünkü birden fazla iş parçacığının aynı anda çağırmayacağını garanti etmek zordur. Öyle değil yapmak için genellikle gerekli örneğini (yani statik olmayan) yöntemler evreli.
Marc Gravell

5
@Florin - ikisi arasında "yığın tabanlı" fark yoktur. Statik alanlar da "dış durum" kadardır ve kesinlikle arayanlar arasında paylaşılacaktır. Örneklerle, farklı iş parçacıklarının farklı örneklere sahip olma şansı vardır (ortak bir desen). Statik ile, hepsinin paylaştığı garanti edilir ([ThreadStatic] hariç).
Marc Gravell

2
@gdoron bir hata mı alıyorsunuz? "Kilit" burada iplik birbirlerinin üzerinde açma önlemek gerekir ...
Marc Gravell

6
@ Nesne asla herkese açık olarak gösterilmezse: yapabilirsiniz. (Çok teorik) risk, başka bir iş parçacığının beklemediğiniz şekillerde kilitlenmesidir.
Marc Gravell

3
@smiron Büyük olasılıkla bir kilidin dışında rasgele de kullanıyorsunuz. Kilitleme, kilitlediğiniz şeye tüm erişimi engellemez - aynı örnekte iki kilit ifadesinin aynı anda çalışmamasını sağlar. Bu nedenle lock (syncObject), tüm random.Next() aramalar da dahilindeyse yardımcı olacaktır lock (syncObject). Açıkladığınız senaryo bile doğru ile oluyor ise lockkullanımı, aynı zamanda son derece muhtemeldir (mesela tek dişli senaryosunda olur Randomustaca bozuldu).
Luaan

118

Uygulamanız boyunca yeniden kullanım kolaylığı için statik bir sınıf yardımcı olabilir.

public static class StaticRandom
{
    private static int seed;

    private static ThreadLocal<Random> threadLocal = new ThreadLocal<Random>
        (() => new Random(Interlocked.Increment(ref seed)));

    static StaticRandom()
    {
        seed = Environment.TickCount;
    }

    public static Random Instance { get { return threadLocal.Value; } }
}

Daha sonra aşağıdaki gibi kodla statik rasgele örneği kullanabilirsiniz

StaticRandom.Instance.Next(1, 100);

62

Mark'ın çözümü her zaman senkronize edilmesi gerektiğinden oldukça pahalı olabilir.

İş parçacığına özgü depolama desenini kullanarak senkronizasyon ihtiyacını karşılayabiliriz:


public class RandomNumber : IRandomNumber
{
    private static readonly Random Global = new Random();
    [ThreadStatic] private static Random _local;

    public int Next(int max)
    {
        var localBuffer = _local;
        if (localBuffer == null) 
        {
            int seed;
            lock(Global) seed = Global.Next();
            localBuffer = new Random(seed);
            _local = localBuffer;
        }
        return localBuffer.Next(max);
    }
}

İki uygulamayı ölçün ve önemli bir fark görmelisiniz.


12
Kilitler, tartışmalı olmadıklarında çok ucuzdur ... ve tartışmalı olsa bile, en ilginç senaryolarda kilidin maliyetini cüce etmek için "şimdi numarayla bir şeyler yap" kodunu beklerim.
Marc Gravell

4
Kabul etti, bu kilitleme problemini çözüyor, ancak bu önemsiz bir soruna hala oldukça karmaşık bir çözüm değil: bir yerine rastgele bir sayı oluşturmak için '' iki '' kod satırı yazmanız gerekiyor. Bu basit bir kod satırı okumak için gerçekten değer mi?
EMP

4
+1 RandomÇekirdeği almak için ek bir küresel örnek kullanmak güzel bir fikirdir. Kodun ThreadLocal<T>.NET 4'te tanıtılan sınıf kullanılarak daha da basitleştirilebileceğini unutmayın (Phil'in aşağıda da yazdığı gibi ).
Groo

40

Buradan cevabım :

Sadece doğru çözümü tekrarlamak :

namespace mySpace
{
    public static class Util
    {
        private static rnd = new Random();
        public static int GetRandom()
        {
            return rnd.Next();
        }
    }
}

Böylece şunları arayabilirsiniz:

var i = Util.GetRandom();

Her yerinde.

Rastgele sayılar üretmek için kesinlikle gerçek bir durumsuz statik yönteme ihtiyacınız varsa , a Guid.

public static class Util
{
    public static int GetRandom()
    {
        return Guid.NewGuid().GetHashCode();
    }
}

Bir wee biraz daha yavaş olacak, ama çok daha rastgele olabilir daha Random.Nextaz deneyimlerime.

Ama değil :

new Random(Guid.NewGuid().GetHashCode()).Next();

Gereksiz nesne oluşturma, özellikle bir döngü altında onu yavaşlatacaktır.

Ve asla :

new Random().Next();

Sadece yavaş değil (bir döngü içinde), rastgele ... bana göre gerçekten iyi değil ..


10
Guid davasına katılmıyorum. Random sınıfı tekdüze bir dağılım uygular. Guid'de durum böyle değil. Kılavuz amaç tekdüze dağıtılmamış benzersiz olmaktır (ve uygulaması çoğu zaman ... rasgeleliğin tersi bazı donanım / makine özelliklerine dayanır).
Askolein

4
Guid kuşağının bütünlüğünü kanıtlayamazsanız, o zaman rastgele kullanmak yanlıştır (ve Hash, tekdüzelikten başka bir adım daha uzak olacaktır). Aynı şekilde, çarpışmalar bir sorun değildir: çarpışma tekdüzeliği. Guid neslinin artık donanımda olmadığına ilişkin olarak
RTFM'ye

5
"Rastgele" nin iki anlayışı vardır: 1. bir olasılık dağılımı ile tanımlanan bir evrimin ardından örüntü eksikliği veya örüntü eksikliği (2, 1'de yer almaktadır). Kılavuz örneğiniz, durum 2'de doğru, durum 2'de değil. Tersi Randomdurumda : sınıf, durum 2 ile eşleşir (dolayısıyla, durum 1 de). Sadece kullanımını değiştirebilir RandomİSS'niz tarafından Guid+Hasheğer değil , o zaman senin durumunda 2. Durum 1 muhtemelen Question yanıtlamak için yeterli olduğunu ve Guid+Hasheserleri cezası. Ancak açıkça söylenmiyor (ps: bu üniforma )
Askolein

2
@Askolein Sadece bazı test verileri için, her iki birkaç toplu çalıştırmak Randomve Guid.NewGuid().GetHashCode()Ent (aracılığıyla fourmilab.ch/random ) ve her iki benzer tesadüfi. "çocuk" lar için tohum üretmek için new Random(Guid.NewGuid().GetHashCode())senkronize edilmiş bir "master" kullanarak olduğu gibi çalışır . Tabii ki, sisteminizin nasıl Rehberler oluşturduğuna bağlıdır - sistemim için, oldukça rastgele ve diğerlerine göre hatta kripto rastgele olabilir. Yani Windows veya MS SQL günümüzde iyi görünüyor. Mono ve / veya mobil cihazlar farklı olabilir. RandomRandom
Luaan

2
@EdB Daha önce yorumlarda söylediğim gibi, Guid (çok sayıda) benzersiz olmakla birlikte, .NET'teki Guid'in GetHashCodedize temsilinden türetilmiştir. Çıktı benim beğenme için oldukça rastgele.
nawfal

27

Rastgele sayılar üretmek için aşağıdaki sınıfı kullanmayı tercih ederim:

byte[] random;
System.Security.Cryptography.RNGCryptoServiceProvider prov = new System.Security.Cryptography.RNGCryptoServiceProvider();
prov.GetBytes(random);

30
Ben aşağı seçmenlerden biri değilim, ancak standart PNRG'nin gerçek bir ihtiyaca hizmet ettiğine dikkat edin - yani bilinen bir tohumdan bir sekansı tekrarlanabilir şekilde üretebilmek. Bazen sırf maliyet gerçek kriptografik RNG çok fazladır. Ve bazen bir kripto RNG gereklidir. Tabii ki atlar.
Marc Gravell

4
Belgelere göre bu sınıf iş parçacığı açısından güvenlidir, bu onun lehine bir şeydir.
Rob Kilisesi

Bunu kullanarak iki rastgele dizenin bir ve aynı olma olasılığı nedir? Eğer dize sadece 3 karakter ise, bu yüksek olasılıkla gerçekleşecek ama 255 karakter uzunluğunda ise aynı rastgele dizeye sahip olmak mümkün mü yoksa bunun algoritmadan gerçekleşemeyeceği garanti mi?
Lyubomir Velchev

16

1) Marc Gravell'in dediği gibi, BİR rasgele jeneratör kullanmaya çalışın. Bunu yapıcıya eklemek her zaman iyidir: System.Environment.TickCount.

2) Bir ipucu. Diyelim ki 100 nesne oluşturmak ve her birinin kendi rastgele oluşturucusuna sahip olması gerektiğini varsayalım (çok kısa sürede rastgele sayıların YÜKLERİNİ hesaplarsanız kullanışlıdır). Bunu bir döngüde (100 nesne oluşturma) yaparsanız, bunu (tam rastgele olmayı sağlamak için) şu şekilde yapabilirsiniz:

int inMyRandSeed;

for(int i=0;i<100;i++)
{
   inMyRandSeed = System.Environment.TickCount + i;
   .
   .
   .
   myNewObject = new MyNewObject(inMyRandSeed);  
   .
   .
   .
}

// Usage: Random m_rndGen = new Random(inMyRandSeed);

Şerefe.


3
System.Environment.TickCount öğesini döngü dışına taşırdım. Eğer yineleme yaparken kenetlenirse, aynı tohuma iki öğe koyulacaktır. Başka bir seçenek, tickcount ile bir i'yi farklı bir şekilde birleştirmek olacaktır (örn. System.Environment.TickCount << 8 + i)
Dolphin

Doğru anlarsam: "System.Environment.TickCount + i" ifadesinin aynı değerle sonuçlanabileceğini mi söylüyorsunuz?
sabiland

EDIT: Tabii ki, döngü içinde TickCount olması gerekmez. Benim hatam :).
sabiland

2
Varsayılan Random()kurucu Random(Environment.TickCount)yine de çağırıyor
Alsty

5

Her yürüttüğünüzde

Random random = new Random (15);

Milyonlarca kez çalıştırmanız önemli değil, her zaman aynı tohumu kullanacaksınız.

Eğer kullanırsan

Random random = new Random ();

Bir bilgisayar korsanı tohumu tahmin ederse ve algoritmanız sisteminizin güvenliği ile ilgiliyse farklı rasgele sayı dizileri elde edersiniz - algoritmanız bozulur. Ben mult yürütüyorum. Bu kurucuda tohum, sistem saati ile belirtilir ve çok kısa bir sürede (milisaniye) birkaç örnek oluşturulursa, aynı tohuma sahip olmaları mümkündür.

Güvenli rasgele sayılara ihtiyacınız varsa sınıfı kullanmalısınız

System.Security.Cryptography.RNGCryptoServiceProvider

public static int Next(int min, int max)
{
    if(min >= max)
    {
        throw new ArgumentException("Min value is greater or equals than Max value.");
    }
    byte[] intBytes = new byte[4];
    using(RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
    {
        rng.GetNonZeroBytes(intBytes);
    }
    return  min +  Math.Abs(BitConverter.ToInt32(intBytes, 0)) % (max - min + 1);
}

Kullanımı:

int randomNumber = Next(1,100);

It does not matter if you execute it millions of times, you will always use the same seed. Tohumu kendiniz belirtmedikçe bu doğru değildir.
LarsTech

Düzeltildi. Teşekkürler Tam olarak söylediğin gibi LarsTech, eğer aynı tohum her zaman belirtilirse, her zaman aynı rasgele sayılar dizisi üretilecektir. Cevabımda hep aynı tohumu kullanırsanız kurucuya parametrelerle başvuruyorum. Random sınıfı yalnızca sözde rastgele sayılar üretir. Birisi algoritmanızda hangi tohumu kullandığınızı öğrenirse, algoritmanızın güvenliğini veya rasgeleliğini tehlikeye atabilir. RNGCryptoServiceProvider sınıfıyla, rasgele sayılara güvenle sahip olabilirsiniz. Zaten düzelttim, düzeltme için çok teşekkür ederim.
Joma

0

Rastgele sınıf değişkenini şöyle bildirmeniz yeterlidir:

    Random r = new Random();
    // ... Get three random numbers.
    //     Here you'll get numbers from 5 to 9
    Console.WriteLine(r.Next(5, 10));

listenizden her seferinde farklı rastgele numaralar almak istiyorsanız

r.Next(StartPoint,EndPoint) //Here end point will not be included

Her seferinde bir Random r = new Random()kez beyan ederek .


Aradığınızda new Random()sistem saatini kullanır, ancak saatin değişmesinden önce tüm kodu arka arkaya iki kez çağırırsanız, aynı rastgele sayıyı alırsınız. Yukarıdaki cevapların bütün mesele bu.
Savage

-1

Burada bir çok çözüm var: sadece sayıları silmek istiyorsanız harfleri silin ve yöntem rastgele ve sonuç uzunluğunu alır.

public String GenerateRandom(Random oRandom, int iLongitudPin)
{
    String sCharacters = "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ123456789";
    int iLength = sCharacters.Length;
    char cCharacter;
    int iLongitudNuevaCadena = iLongitudPin; 
    String sRandomResult = "";
    for (int i = 0; i < iLongitudNuevaCadena; i++)
    {
        cCharacter = sCharacters[oRandom.Next(iLength)];
        sRandomResult += cCharacter.ToString();
    }
    return (sRandomResult);
}

Temel sorun hala aynı - bir Randomörnek geçiyorsunuz, ancak hala arayan kişinin paylaşılan bir örnek oluşturmasını bekliyorsunuz. Arayan her seferinde yeni bir örnek oluşturursa ve saat değişmeden önce kod iki kez yürütülürse, aynı rasgele sayıyı alırsınız. Dolayısıyla bu cevap hala hatalı olabilecek varsayımlar yapmaktadır.
Savage

Ayrıca, rastgele sayılar üretmek için bir yönteme sahip olmanın tüm amacı kapsülleme - çağrı yönteminin uygulama hakkında endişelenmesi gerekmediği, sadece rastgele bir sayı geri almakla ilgileniyor
Savage
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.