web-dev-qa-db-fra.com

Générer des valeurs uniques et aléatoires C #

Je cherche depuis un moment et j'ai du mal à trouver cela. J'essaie de générer plusieurs nombres aléatoires et uniques: C #. J'utilise System.Random et une graine datetime.now.ticks:

public Random a = new Random(DateTime.Now.Ticks.GetHashCode());
private void NewNumber()
  {
     MyNumber = a.Next(0, 10);
  }

J'appelle régulièrement NewNumber (), mais le problème est que je reçois souvent des numéros répétés. Certaines personnes ont suggéré que, parce que je déclarais le hasard chaque fois que je le faisais, cela ne produirait pas un nombre aléatoire, alors je mets la déclaration en dehors de ma fonction. Des suggestions ou de meilleurs moyens que d'utiliser System.Random? Je vous remercie

23
Christian Peut

J'appelle régulièrement NewNumber (), mais le problème, c'est que je reçois souvent nombres répétés.

Random.Next ne garantit pas que le numéro soit unique. De plus, votre plage va de 0 à 10 et vous obtiendrez probablement des doublons. Peut-être que vous pouvez configurer une liste de int et insérer des nombres aléatoires dans la liste après avoir vérifié si elle ne contient pas le doublon. Quelque chose comme:

public Random a = new Random(); // replace from new Random(DateTime.Now.Ticks.GetHashCode());
                                // Since similar code is done in default constructor internally
public List<int> randomList = new List<int>();
int MyNumber = 0;
private void NewNumber()
{
    MyNumber = a.Next(0, 10);
    if (!randomList.Contains(MyNumber))
        randomList.Add(MyNumber);
}
21
Habib

Vous pouvez essayer de mélanger un tableau d'ints possibles si votre plage est comprise entre 0 et 9. Cela évite également tout conflit lors de la génération de nombres.

var nums = Enumerable.Range(0, 10).ToArray();
var rnd = new Random();

// Shuffle the array
for (int i = 0;i < nums.Length;++i)
{
    int randomIndex = rnd.Next(nums.Length);
    int temp = nums[randomIndex];
    nums[randomIndex] = nums[i];
    nums[i] = temp;
}

// Now your array is randomized and you can simply print them in order
for (int i = 0;i < nums.Length;++i)
    Console.WriteLine(nums[i]);
15
itsme86

Je publie une implémentation correcte d'un algorithme de mélange, car l'autre qui est posté ici ne produit pas de mélange uniforme.

Comme l’autre réponse indique, pour que de petits nombres de valeurs soient randomisés, vous pouvez simplement remplir un tableau avec ces valeurs, mélanger le tableau, puis utiliser le nombre de valeurs souhaité.

Ce qui suit est une implémentation du Fisher-Yates Shuffle (aussi connu sous le nom de Knuth Shuffle). (Lisez la section "erreurs d'implémentation" de ce lien (recherchez "en sélectionnant toujours j dans toute la plage d'indices de tableau valides à chaque itération") pour consulter un aperçu de ce qui ne va pas avec l'autre implémentation publiée ici.)

using System;
using System.Collections.Generic;

namespace ConsoleApplication2
{
    static class Program
    {
        static void Main(string[] args)
        {
            Shuffler shuffler = new Shuffler();
            List<int> list = new List<int>{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            shuffler.Shuffle(list);

            foreach (int value in list)
            {
                Console.WriteLine(value);
            }
        }
    }

    /// <summary>Used to shuffle collections.</summary>

    public class Shuffler
    {
        /// <summary>Creates the shuffler with a <see cref="MersenneTwister"/> as the random number generator.</summary>

        public Shuffler()
        {
            _rng = new Random();
        }

        /// <summary>Shuffles the specified array.</summary>
        /// <typeparam name="T">The type of the array elements.</typeparam>
        /// <param name="array">The array to shuffle.</param>

        public void Shuffle<T>(IList<T> array)
        {
            for (int n = array.Count; n > 1; )
            {
                int k = _rng.Next(n);
                --n;
                T temp = array[n];
                array[n] = array[k];
                array[k] = temp;
            }
        }

        private System.Random _rng;
    }
}
9
Matthew Watson

NOTE: Je ne recommande pas ça :). Voici un "one liner" aussi:

//This code generates numbers between 1 - 100 and then takes 10 of them.
var result = Enumerable.Range(1,101).OrderBy(g => Guid.NewGuid()).Take(10).ToArray();
9
JOSEFtw

En fonction de ce que vous voulez vraiment, vous pouvez faire quelque chose comme ceci:

using System;
using System.Collections.Generic;
using System.Linq;

namespace SO14473321
{
    class Program
    {
        static void Main()
        {
            UniqueRandom u = new UniqueRandom(Enumerable.Range(1,10));
            for (int i = 0; i < 10; i++)
            {
                Console.Write("{0} ",u.Next());
            }
        }
    }

    class UniqueRandom
    {
        private readonly List<int> _currentList;
        private readonly Random _random = new Random();

        public UniqueRandom(IEnumerable<int> seed)
        {
            _currentList = new List<int>(seed);
        }

        public int Next()
        {
            if (_currentList.Count == 0)
            {
                throw new ApplicationException("No more numbers");
            }

            int i = _random.Next(_currentList.Count);
            int result = _currentList[i];
            _currentList.RemoveAt(i);
            return result;
        }
    }
}
1
Andrew Savinykh

Et voici ma version de la recherche de N nombres uniques aléatoires à l’aide de HashSet . Cela semble assez simple, puisque HashSet ne peut contenir que des éléments différents . C’est intéressant - serait-il plus rapide d’utiliser List ou Shuffler?

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class RnDHash
    {
        static void Main()
        {
            HashSet<int> rndIndexes = new HashSet<int>();
            Random rng = new Random();
            int maxNumber;
            Console.Write("Please input Max number: ");
            maxNumber = int.Parse(Console.ReadLine());
            int iter = 0;
            while (rndIndexes.Count != maxNumber)
            {
                int index = rng.Next(maxNumber);
                rndIndexes.Add(index);
                iter++;
            }
            Console.WriteLine("Random numbers were found in {0} iterations: ", iter);
            foreach (int num in rndIndexes)
            {
                Console.WriteLine(num);
            }
            Console.ReadKey();
        }
    }
}
0
Vasily Novsky

J'ai remarqué que la réponse acceptée continuait à ajouter des int à la liste et continuait à les vérifier avec if (!randomList.Contains(MyNumber)) et je pense que cela ne s'adapte pas bien, surtout si vous continuez à demander de nouveaux numéros.

Je ferais le contraire.

  1. Générer la liste au démarrage, de manière linéaire
  2. Obtenir un index aléatoire de la liste
  3. Supprimer l'int trouvé dans la liste

Cela nécessiterait un peu plus de temps au démarrage, mais évoluerait beaucoup mieux.

public class RandomIntGenerator
{
    public Random a = new Random();
    private List<int> _validNumbers;

    private RandomIntGenerator(int desiredAmount, int start = 0)
    {
        _validNumbers = new List<int>();
        for (int i = 0; i < desiredAmount; i++)
            _validNumbers.Add(i + start);
    }

    private int GetRandomInt()
    {
        if (_validNumbers.Count == 0)
        {
            //you could throw an exception here
            return -1;
        }
        else
        {
            var nextIndex = a.Next(0, _validNumbers.Count - 1);
            var number    = _validNumbers[nextIndex];
            _validNumbers.RemoveAt(nextIndex);
            return number;
        }
    }
}
0
Jack Mariani

Cochez cette méthode prête à l'emploi: indiquez une plage et un nombre de nombres que vous souhaitez obtenir.

public static int[] getUniqueRandomArray(int min, int max, int count) {
    int[] result = new int[count];
    List<int> numbersInOrder = new List<int>();
    for (var x = min; x < max; x++) {
        numbersInOrder.Add(x);
    }
    for (var x = 0; x < count; x++) {
        var randomIndex = Random.Range(0, numbersInOrder.Count);
        result[x] = numbersInOrder[randomIndex];
        numbersInOrder.RemoveAt(randomIndex);
    }

    return result;
}
0
Evren Ozturk