Question Générateur de nombres aléatoires générant seulement un nombre aléatoire


J'ai la fonction suivante:

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

Comment je l'appelle:

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

Si je fais cette boucle avec le débogueur pendant l'exécution, j'obtiens des valeurs différentes (ce que je veux). Cependant, si je mets un point d'arrêt à deux lignes en dessous de ce code, tous les membres du tableau "mac" ont la même valeur.

Pourquoi cela arrive-t-il?


647
2018-04-20 12:11


origine


Réponses:


Chaque fois que vous faites new Random() il est initialisé en utilisant l'horloge. Cela signifie que dans une boucle serrée vous obtenez la même valeur beaucoup de fois. Vous devriez garder un seul Random instance et continuer à utiliser Next sur le même exemple.

//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 (voir les commentaires): pourquoi avons-nous besoin d'un lock ici?

Fondamentalement, Next va changer l'état interne de la Random exemple. Si nous faisons cela en même temps à partir de plusieurs threads, vous pourrait argumenter "nous venons de rendre le résultat encore plus aléatoire", mais ce que nous sommes réellement faire est potentiellement briser l'implémentation interne, et nous pourrions également commencer à obtenir les mêmes numéros à partir de différents threads, qui pourrait être un problème - et pourrait ne pas. La garantie de ce qui se passe à l'interne est la plus grande question, cependant; depuis Random Est-ce que ne pas faire des garanties de sécurité des threads. Il y a donc deux approches valables:

  • synchroniser afin de ne pas y accéder en même temps à partir de différents threads
  • utiliser différent Random instances par thread

L'une ou l'autre peut être bonne; mais en mutexing unique instance de plusieurs appelants en même temps ne fait que demander des ennuis.

le lock réalise la première (et la plus simple) de ces approches; Cependant, une autre approche pourrait être:

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

C'est alors par thread, donc vous n'avez pas besoin de synchroniser.


885
2018-04-20 12:12



Pour faciliter la réutilisation tout au long de votre application, une classe statique peut vous aider.

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

Vous pouvez utiliser ensuite utiliser une instance aléatoire statique avec un code tel que

StaticRandom.Instance.Next(1, 100);

94
2017-07-13 15:25



La solution de Mark peut être assez coûteuse car elle doit être synchronisée à chaque fois.

Nous pouvons contourner le besoin de synchronisation en utilisant le modèle de stockage spécifique au thread:


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

Mesurez les deux implémentations et vous devriez voir une différence significative.


49
2018-06-23 11:26



Ma réponse de ici:

Juste réitérer la bonne solution:

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

Vous pouvez donc appeler:

var i = Util.GetRandom();

tout au long de.

Si vous avez strictement besoin d'une vraie méthode statique sans état pour générer des nombres aléatoires, vous pouvez compter sur un Guid.

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

Ça va être un petit peu plus lent, mais peut être beaucoup plus aléatoire que Random.Next, au moins de mon expérience.

Mais ne pas:

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

La création d'objet inutile va ralentir, surtout sous une boucle.

Et jamais:

new Random().Next();

Non seulement c'est plus lent (à l'intérieur d'une boucle), son caractère aléatoire est ... bien pas vraiment bien selon moi ..


30
2018-03-31 12:34



Je préfère utiliser la classe suivante pour générer des nombres aléatoires:

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

21
2018-04-20 12:25



1) Comme l'a dit Marc Gravell, essayez d'utiliser UN générateur aléatoire. C'est toujours cool d'ajouter ceci au constructeur: System.Environment.TickCount.

2) Un conseil. Disons que vous voulez créer 100 objets et supposer que chacun d'entre eux devrait avoir son propre générateur aléatoire (pratique si vous calculez des CHARGES de nombres aléatoires sur une période de temps très courte). Si vous le faisiez en boucle (génération de 100 objets), vous pourriez le faire comme ça (pour assurer le caractère complètement aléatoire):

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

À votre santé.


12



Il y a beaucoup de solutions, ici une: si vous voulez seulement le nombre effacer les lettres et la méthode reçoit un aléatoire et la longueur du résultat.

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