Generator liczb losowych generuje tylko jedną liczbę losową

Mam następującą funkcję:

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

Jak to nazywam:

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

Jeśli wykonam tę pętlę z debuggerem podczas wykonywania, otrzymam inne wartości (co jest tym, czego chcę). Jeśli jednak umieszczę punkt przerwania dwa wiersze poniżej tego kodu, wszyscy członkowie tablicy " mac " mają taką samą wartość.

Dlaczego tak się dzieje?
 662
Author: Taryn, 2009-04-20

7 answers

Za każdym razem, gdy robisz {[2] } jest inicjowana za pomocą zegara. Oznacza to, że w ciasnej pętli otrzymujesz tę samą wartość wiele razy. Należy zachować pojedynczą instancję Random i używać Next na tej samej instancji.

//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);
    }
}

Edit (Zobacz komentarze): po co nam lock tutaj?

Zasadniczo, Next zmieni stan wewnętrzny instancji Random. Jeśli zrobimy to w tym samym czasie z wielu wątków, możesz argumentować " właśnie zrobiliśmy wynik jest jeszcze bardziej losowy", ale to, co robimy w rzeczywistości, potencjalnie łamie wewnętrzną implementację, a także możemy zacząć uzyskiwać te same liczby z różnych wątków, co może być problemem - a może nie. Gwarancja tego, co dzieje się wewnętrznie, jest jednak większym problemem; ponieważ Random Nie daje jakiekolwiek gwarancje bezpieczeństwa wątku. Tak więc istnieją dwa ważne podejścia:

  • synchronizować, abyśmy nie mieli do niego dostępu w ten sam czas z różnych wątków
  • użyj różnych instancji Random na wątek

Albo może być w porządku; ale muteksowanie pojedynczej instancji od wielu wywołujących jednocześnie jest tylko pytaniem o kłopoty.

lock osiąga pierwszy (i prostszy) z tych podejść; jednak inne podejście może być:

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

To jest na wątek, więc nie trzeba synchronizować.

 904
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-29 06:45:03

Dla ułatwienia ponownego użycia w całej aplikacji może pomóc Klasa statyczna.

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; } }
}

Możesz użyć wtedy statycznej instancji losowej z kodem takim jak

StaticRandom.Instance.Next(1, 100);
 98
Author: Phil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-27 22:05:02

Rozwiązanie marka może być dość drogie, ponieważ wymaga synchronizacji za każdym razem.

Możemy obejść potrzebę synchronizacji za pomocą specyficznego dla wątku wzorca przechowywania:


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);
    }
}

Zmierz dwie implementacje, a zobaczysz znaczącą różnicę.

 52
Author: Hans Malherbe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-23 11:26:10

Moja odpowiedź z tutaj :

Wystarczy powtórzyć właściwe rozwiązanie :

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

Więc możesz zadzwonić:

var i = Util.GetRandom();
/ Align = "left" /

Jeśli do generowania liczb losowych potrzebujesz statycznej metody True stateless, możesz polegać na Guid.

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

to będzie trochę wolniejsze, ale może być znacznie bardziej losowe niż Random.Next, przynajmniej z mojego doświadczenia.

Ale Nie :

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

The niepotrzebne tworzenie obiektów spowoduje, że będzie wolniej, zwłaszcza pod pętlą.

Inigdy :

new Random().Next();
Nie dość, że jest wolniejszy (wewnątrz pętli), to jego losowość jest... według mnie niezbyt dobrze..
 31
Author: nawfal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:07

Wolałbym użyć następującej klasy do generowania liczb losowych:

byte[] random;
System.Security.Cryptography.RNGCryptoServiceProvider prov = new System.Security.Cryptography.RNGCryptoServiceProvider();
prov.GetBytes(random);
 21
Author: fARcRY,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-22 17:43:17

1) Jak powiedział Marc Gravell, spróbuj użyć jednego generatora losowego. Zawsze fajnie jest dodać to do konstruktora: System.Środowisko.TickCount.

2) Jedna wskazówka. Załóżmy, że chcesz utworzyć 100 obiektów i załóżmy, że każdy z nich powinien mieć swój własny generator losowy (przydatne, jeśli obliczysz ładunki liczb losowych w bardzo krótkim okresie czasu). Jeśli chcesz to zrobić w pętli (generowanie 100 obiektów), możesz to zrobić w ten sposób (aby zapewnić pełna losowość):

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);
Zdrówko.
 12
Author: sabiland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-25 17:24:34

Istnieje wiele rozwiązań, tutaj jeden: jeśli chcesz tylko numer Wymaż litery i metoda otrzymuje losową i długość wyniku.

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);
}
 1
Author: Marztres,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-04 05:13:31