web-dev-qa-db-fra.com

Générateur de nombres aléatoires ne générant qu'un seul 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 j'exécute 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 se produit-il?

728
Ivan Prodanov

Chaque fois que vous faites new Random(), il est initialisé à l'aide de l'horloge. Cela signifie que dans une boucle serrée, vous obtenez la même valeur plusieurs fois. Vous devez conserver une seule instance Random et continuer à utiliser Next sur la même instance .

//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'une lock ici?

Fondamentalement, Next va modifier l'état interne de l'instance Random. Si nous faisons cela en même temps à partir de plusieurs threads, vous pourriez argumenter "nous venons de rendre le résultat encore plus aléatoire", mais ce que nous sommes en fait est potentiellement en train de casser l'implémentation interne, et nous pourrions aussi commencer à obtenir les mêmes numéros de différents threads, ce qui pourrait être un problème - et peut-être pas. La garantie de ce qui se passe en interne est le problème le plus important; puisque Random ne fait pas de garantie de sécurité du thread. Il existe donc deux approches valables:

  • synchroniser afin que nous n'y accédons pas en même temps à partir de différents threads
  • utiliser différentes Random instances par thread

Soit peut être bien; mais mutexer une instance unique de plusieurs appelants en même temps ne fait que poser problème.

La 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, vous n'avez donc pas besoin de synchroniser.

994
Marc Gravell

Pour faciliter la réutilisation dans votre application, une classe statique peut 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 du code tel que

StaticRandom.Instance.Next(1, 100);
112
Phil

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

Nous pouvons éviter 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);
    }
}

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

60
Hans Malherbe

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 vous fier à un Guid.

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

Cela va être un peu plus lent, mais peut être beaucoup plus aléatoire que Random.Next, du moins d'après mon expérience.

Mais pas :

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

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

Et jamais :

new Random().Next();

Non seulement il est plus lent (dans une boucle), mais son caractère aléatoire est… enfin, pas vraiment bon selon moi….

37
nawfal

Je préférerais 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);
24
fARcRY

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. Supposons que vous vouliez créer 100 objets et supposons que chacun d’eux ait son propre générateur d’aléatoire (pratique si vous calculez des charges de nombres aléatoires sur une très courte période). Si vous faites cela dans une boucle (génération de 100 objets), vous pouvez le faire comme ceci (pour assurer un caractère totalement 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é.

14
sabiland

Chaque fois que vous exécutez

Random random = new Random (15);

Peu importe si vous l'exécutez des millions de fois, vous utiliserez toujours la même graine.

Si tu utilises

Random random = new Random ();

Vous obtenez une séquence de nombres aléatoires différente, si un pirate informatique devine l'origine et que votre algorithme est lié à la sécurité de votre système, votre algorithme est cassé. Je vous exécutez mult. Dans ce constructeur, la graine est spécifiée par l'horloge système et si plusieurs instances sont créées dans un laps de temps très court (millisecondes), il est possible qu'elles aient la même graine.

Si vous avez besoin de nombres aléatoires sûrs, vous devez utiliser la classe

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

Usage:

int randomNumber = Next(1,100);
3
Joma

déclarez simplement la variable de classe Random comme ceci:

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

si vous voulez obtenir un nombre aléatoire différent à chaque fois dans votre liste, utilisez

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

Chaque fois en déclarant Random r = new Random() une fois.

1
Abdul

Il existe de nombreuses solutions, en voici une: si vous voulez que seul le nombre efface les lettres et que la méthode reçoive une longueur aléatoire et le 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);
}
0
Marztres