843 Stimmen

Zufallszahlengenerator erzeugt nur eine Zufallszahl

Ich habe die folgende Funktion:

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

Wie ich es nenne:

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

Wenn ich diese Schleife mit dem Debugger während der Laufzeit durchlaufe, erhalte ich andere Werte (was ich ja auch will). Wenn ich jedoch zwei Zeilen unterhalb dieses Codes einen Haltepunkt setze, werden alle Mitglieder der mac Array haben den gleichen Wert.

Warum ist das so?

27 Stimmen

Mit new Random().Next((int)0xFFFF, (int)0xFFFFFF) % 256); ergibt keine besseren "Zufallszahlen" als .Next(0, 256)

2 Stimmen

Sie finden vielleicht dieses NuGet-Paket hilfreich. Es bietet eine statische Rand.Next(int, int) Methode, die einen statischen Zugriff auf Zufallswerte ermöglicht, ohne Sperren oder das Problem der Wiederverwendung von Seeds zu verursachen

1148voto

Marc Gravell Punkte 970173

Jedes Mal, wenn Sie new Random() er wird mit Hilfe der Uhr initialisiert. Das bedeutet, dass Sie in einer engen Schleife viele Male denselben Wert erhalten. Sie sollten einen einzigen Zufällig Instanz und verwenden Sie weiterhin Weiter über die dieselbe Instanz.

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

Bearbeiten (siehe Kommentare): Warum brauchen wir eine lock hier?

Im Grunde genommen, Next wird den internen Zustand des Random Instanz. Wenn wir das gleichzeitig von mehreren Threads aus tun, können Sie pourrait "Wir haben das Ergebnis nur noch zufälliger gemacht", aber was wir tun, ist eigentlich Dies könnte die interne Implementierung zerstören, und wir könnten auch anfangen, dieselben Zahlen von verschiedenen Threads zu erhalten, was puede ein Problem sein - und vielleicht auch nicht. Die Garantie dessen, was intern geschieht, ist jedoch das größere Problem; denn Random hace no keine Garantien für die Sicherheit von Threads geben. Es gibt also zwei gültige Ansätze:

  • Synchronisieren, damit wir nicht von verschiedenen Threads aus gleichzeitig darauf zugreifen
  • Verwenden Sie verschiedene Random Instanzen pro Thread

Beides kann in Ordnung sein; aber das Mutexing einer einzeln Instanz von mehreren Anrufern zur gleichen Zeit ist einfach zu viel des Guten.

Le site lock erreicht den ersten (und einfacheren) dieser Ansätze; ein anderer Ansatz könnte jedoch sein:

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

dies ist dann pro Thread, so dass Sie nicht synchronisieren müssen.

1 Stimmen

Ich bin nicht sicher, dass dies die Antwort in diesem Fall ist, da die Frage besagt, dass die Funktion ordnungsgemäß funktioniert, wenn es keine Haltepunkte in der Nähe dieses Segments des Codes gibt. Natürlich stimme ich zu, dass er eine Instanz des Random-Objekts verwenden sollte, aber ich denke nicht, dass dies seine Frage beantwortet.

0 Stimmen

@John - es ist schon schwer genug, ihn im Auge zu behalten (meist als Punkt in der mittleren Entfernung).

26 Stimmen

Generell sollten alle statischen Methoden thread-sicher gemacht werden, da es schwer zu garantieren ist, dass sie nicht von mehreren Threads gleichzeitig aufgerufen werden. Es ist no in der Regel notwendig, um die Instanz (d.h. nicht-statische) Methoden thread-safe.

137voto

Phil Punkte 2258

Um die Wiederverwendung in Ihrer Anwendung zu erleichtern, kann eine statische Klasse hilfreich sein.

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

Sie können dann die statische Zufallsinstanz mit Code wie

StaticRandom.Instance.Next(1, 100);

70voto

Hans Malherbe Punkte 2908

Die Lösung von Mark kann ziemlich teuer sein, da sie jedes Mal synchronisiert werden muss.

Wir können die Notwendigkeit der Synchronisierung umgehen, indem wir das thread-spezifische Speichermuster verwenden:

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

Messen Sie die beiden Implementierungen und Sie sollten einen deutlichen Unterschied feststellen.

16 Stimmen

Schlösser sind sehr billig, wenn sie nicht angefochten werden... und selbst wenn sie angefochten werden, würde ich erwarten, dass der "Jetzt mach was mit der Nummer"-Code die Kosten des Schlosses in den meisten interessanten Szenarien in den Schatten stellt.

4 Stimmen

Einverstanden, das löst das Sperrproblem, aber ist das nicht immer noch eine hochkomplizierte Lösung für ein triviales Problem: dass man "zwei" Codezeilen schreiben muss, um eine Zufallszahl zu erzeugen, anstatt einer. Ist es das wirklich wert, wenn man sich das Lesen einer einfachen Codezeile sparen kann?

5 Stimmen

+1 Verwendung einer zusätzlichen globalen Random Instanz für den Erhalt des Saatguts ist eine gute Idee. Beachten Sie auch, dass der Code weiter vereinfacht werden kann, wenn Sie die ThreadLocal<T> Klasse, die in .NET 4 eingeführt wurde (wie Phil auch schrieb unten ).

43voto

nawfal Punkte 65966

Meine Antwort von aquí :

Nur zur Wiederholung die richtige Lösung :

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

Sie können also anrufen:

var i = Util.GetRandom();

überall.

Wenn Sie unbedingt eine echte zustandslose statische Methode benötigen um Zufallszahlen zu generieren, können Sie sich auf eine Guid .

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

Es wird ein bisschen langsamer sein, aber es kann viel mehr Zufall sein. als Random.Next zumindest meiner Erfahrung nach.

Aber no :

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

Die unnötige Objekterzeugung wird es langsamer machen, besonders in einer Schleife.

Y niemals :

new Random().Next();

Es ist nicht nur langsamer (innerhalb einer Schleife), seine Zufälligkeit ist... nun, meiner Meinung nach nicht wirklich gut.

17 Stimmen

Ich bin mit dem Fall Guid nicht einverstanden. Die Klasse Random implementiert eine Gleichverteilung. Das ist bei Guid nicht der Fall. Das Ziel von Guid ist es, einzigartig und nicht gleichmäßig verteilt zu sein (und seine Implementierung basiert meistens auf einer Hardware-/Maschineneigenschaft, die das Gegenteil von ... Zufälligkeit ist).

0 Stimmen

@Askolein eins, da bin ich mir nicht sicher. Es geht um den Hash der Guid und nicht um die Guid selbst, und der ist ziemlich zufällig. Haben Sie die beiden getestet? Ich finde viel mehr Kollisionen mit Random.Next . Zweitens: MS generiert keine Guid mehr auf der Grundlage von Hardware-Eigenschaften.

6 Stimmen

Wenn man die Einheitlichkeit der Guid-Generierung nicht nachweisen kann, dann ist es falsch, sie als Zufallsgenerator zu verwenden (und der Hash wäre ein weiterer Schritt weg von der Einheitlichkeit). Auch Kollisionen sind kein Problem, sondern die Einheitlichkeit der Kollisionen. Was die Guid-Erzeugung betrifft, die nicht mehr auf Hardware basiert, so muss ich RTFM betreiben, mein Fehler (gibt es eine Referenz?)

29voto

fARcRY Punkte 2310

Ich würde lieber die folgende Klasse verwenden, um Zufallszahlen zu erzeugen:

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

32 Stimmen

Ich gehöre nicht zu den Ablehnern, stelle aber fest, dass die Standard-PNRG einem echten Bedürfnis dienen, nämlich eine Sequenz aus einem bekannten Keim wiederholbar zu reproduzieren. Manchmal ist die schiere Kosten eines echten kryptografischen RNG ist zu viel. Und manchmal ist ein Krypto-RNG notwendig. Pferde für Kurse, sozusagen.

4 Stimmen

Selon le Dokumentation Diese Klasse ist thread-sicher, das spricht also für sie.

0 Stimmen

Wie groß ist die Wahrscheinlichkeit, dass zwei zufällige Zeichenketten auf diese Weise ein und dieselbe sind? Wenn die Zeichenfolge ist nur 3 Zeichen Ich denke, dies wird mit hoher Wahrscheinlichkeit passieren, aber was ist, wenn 255 Zeichen Länge ist es möglich, die gleiche zufällige Zeichenfolge haben oder ist garantiert, dass dies nicht aus dem Algorithmus passieren kann?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X