repetir numeros llenar lista generar desordenar con arreglo aleatorios c# generic-list

c# - numeros - Aleatorizar una lista<T>



llenar un arreglo con numeros aleatorios c# (18)

Aquí hay un Shuffler eficiente que devuelve una matriz de bytes de valores barajados. Nunca baraja más de lo que se necesita. Se puede reiniciar desde donde lo dejó previamente. Mi implementación real (no se muestra) es un componente de MEF que permite un shuffler de reemplazo especificado por el usuario.

public byte[] Shuffle(byte[] array, int start, int count) { int n = array.Length - start; byte[] shuffled = new byte[count]; for(int i = 0; i < count; i++, start++) { int k = UniformRandomGenerator.Next(n--) + start; shuffled[i] = array[k]; array[k] = array[start]; array[start] = shuffled[i]; } return shuffled; }

`

¿Cuál es la mejor manera de aleatorizar el orden de una lista genérica en C #? Tengo un conjunto finito de 75 números en una lista a la que me gustaría asignar un orden aleatorio para dibujarlos para una aplicación de tipo de lotería.


Aquí hay una manera segura de hacer esto:

public static class EnumerableExtension { private static Random globalRng = new Random(); [ThreadStatic] private static Random _rng; private static Random rng { get { if (_rng == null) { int seed; lock (globalRng) { seed = globalRng.Next(); } _rng = new Random(seed); } return _rng; } } public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> items) { return items.OrderBy (i => rng.Next()); } }


Este es mi método preferido de orden aleatorio cuando es conveniente no modificar el original. Es una variante del algoritmo "de adentro hacia afuera" de Fisher-Yates que funciona en cualquier secuencia enumerable (la longitud de la source no necesita ser conocida desde el principio).

public static IList<T> NextList<T>(this Random r, IEnumerable<T> source) { var list = new List<T>(); foreach (var item in source) { var i = r.Next(list.Count + 1); if (i == list.Count) { list.Add(item); } else { var temp = list[i]; list[i] = item; list.Add(temp); } } return list; }

Este algoritmo también se puede implementar asignando un rango de 0 a length - 1 y agotando aleatoriamente los índices intercambiando el índice elegido al azar con el último índice hasta que todos los índices se hayan elegido exactamente una vez. Este código anterior cumple exactamente lo mismo pero sin la asignación adicional. Que es bastante limpio.

Con respecto a la clase Random , es un generador de números de propósito general (y si estuviera ejecutando una lotería consideraría usar algo diferente). También se basa en un valor semilla basado en el tiempo de forma predeterminada. Un pequeño alivio del problema es sembrar la clase Random con el RNGCryptoServiceProvider o usted podría usar el RNGCryptoServiceProvider en un método similar a este (ver a continuación) para generar valores de punto flotante doble aleatorios elegidos uniformemente, pero ejecutar una lotería requiere bastante comprensión aleatoria y La naturaleza de la fuente de aleatoriedad.

var bytes = new byte[8]; _secureRng.GetBytes(bytes); var v = BitConverter.ToUInt64(bytes, 0); return (double)v / ((double)ulong.MaxValue + 1);

El punto de generar un doble aleatorio (entre 0 y 1 exclusivamente) es usar para escalar a una solución entera. Si necesitas elegir algo de una lista basada en una doble x aleatoria que siempre va a ser 0 <= x && x < 1 es sencillo.

return list[(int)(x * list.Count)];

¡Disfrutar!


Estoy un poco sorprendido por todas las versiones torpes de este algoritmo simple aquí. Fisher-Yates (o Knuth shuffle) es un poco complicado pero muy compacto. Si vas a Wikipedia, verías una versión de este algoritmo que tiene un ciclo inverso inverso y mucha gente parece no entender por qué sucede lo contrario. La razón clave es que esta versión del algoritmo asume que el generador de números aleatorios Random(n) a su disposición tiene las siguientes dos propiedades:

  1. Acepta n como un solo parámetro de entrada.
  2. Devuelve el número de 0 a n inclusive .

Sin embargo, el generador de números aleatorios .Net no satisface la propiedad # 2. El Random.Next(n) lugar devuelve el número de 0 a n-1 inclusive. Si intenta usar for-loop en reversa, entonces deberá llamar a Random.Next(n+1) que agrega una operación adicional.

Sin embargo, el generador de números aleatorios .Net tiene otra función agradable Random.Next(a,b) que devuelve a a b-1 inclusive. Esto encaja perfectamente perfectamente con la implementación de este algoritmo que tiene un bucle for normal. Así que sin más preámbulos, aquí está la implementación correcta, eficiente y compacta:

public static void Shuffle<T>(this IList<T> list, Random rnd) { for(var i=0; i < list.Count; i++) list.Swap(i, rnd.Next(i, list.Count)); } public static void Swap<T>(this IList<T> list, int i, int j) { var temp = list[i]; list[i] = list[j]; list[j] = temp; }


La idea es obtener un objeto anónimo con un artículo y un orden aleatorio y luego reordenar los artículos por este orden y valor de retorno:

var result = items.Select(x => new { value = x, order = rnd.Next() }) .OrderBy(x => x.order).Select(x => x.value).ToList()


Método de extensión para IEnumerable:

public static IEnumerable<T> Randomize<T>(this IEnumerable<T> source) { Random rnd = new Random(); return source.OrderBy<T, int>((item) => rnd.Next()); }


Publicación antigua con seguridad, pero acabo de usar un GUID.

Items = Items.OrderBy(o => Guid.NewGuid().ToString()).ToList();

Un GUID es siempre único, y se regenera cada vez que el resultado cambia cada vez.


Puedes lograrlo utilizando este sencillo método de extensión.

public static class IEnumerableExtensions { public static IEnumerable<t> Randomize<t>(this IEnumerable<t> target) { Random r = new Random(); return target.OrderBy(x=>(r.Next())); } }

y puedes usarlo haciendo lo siguiente

// use this on any collection that implements IEnumerable! // List, Array, HashSet, Collection, etc List<string> myList = new List<string> { "hello", "random", "world", "foo", "bar", "bat", "baz" }; foreach (string s in myList.Randomize()) { Console.WriteLine(s); }


Si no te importa usar dos Lists , esta es probablemente la forma más fácil de hacerlo, pero probablemente no la más eficiente o impredecible:

List<int> xList = new List<int>() { 1, 2, 3, 4, 5 }; List<int> deck = new List<int>(); foreach (int xInt in xList) deck.Insert(random.Next(0, deck.Count + 1), xInt);


Si solo necesitamos mezclar los elementos en un orden completamente aleatorio (solo para mezclar los elementos en una lista), prefiero este código simple pero efectivo que ordena los artículos por guía ...

var shuffledcards = cards.OrderBy(a => Guid.NewGuid()).ToList();


Si tiene un número fijo (75), puede crear una matriz con 75 elementos, luego enumerar su lista, moviendo los elementos a posiciones aleatorias en la matriz. Puede generar el mapeo del número de lista con el índice de matriz utilizando la reproducción aleatoria de Fisher-Yates .


Suelo usar:

var list = new List<T> (); fillList (list); var randomizedList = new List<T> (); var rnd = new Random (); while (list.Count != 0) { var index = rnd.Next (0, list.Count); randomizedList.Add (list [index]); list.RemoveAt (index); }


Un enfoque muy simple para este tipo de problema es usar un número de elementos de intercambio aleatorio en la lista.

En el pseudocódigo esto se vería así:

do r1 = randomPositionInList() r2 = randomPositionInList() swap elements at index r1 and index r2 for a certain number of times


Una simple modificación de la respuesta aceptada que devuelve una nueva lista en lugar de trabajar en el lugar, y acepta el IEnumerable<T> más general que muchos otros métodos de Linq.

private static Random rng = new Random(); /// <summary> /// Returns a new list where the elements are randomly shuffled. /// Based on the Fisher-Yates shuffle, which has O(n) complexity. /// </summary> public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> list) { var source = list.ToList(); int n = source.Count; var shuffled = new List<T>(n); shuffled.AddRange(source); while (n > 1) { n--; int k = rng.Next(n + 1); T value = shuffled[k]; shuffled[k] = shuffled[n]; shuffled[n] = value; } return shuffled; }


EDITAR El RemoveAt es una debilidad en mi versión anterior. Esta solución supera eso.

public static IEnumerable<T> Shuffle<T>( this IEnumerable<T> source, Random generator = null) { if (generator == null) { generator = new Random(); } var elements = source.ToArray(); for (var i = elements.Length - 1; i >= 0; i--) { var swapIndex = generator.Next(i + 1); yield return elements[swapIndex]; elements[swapIndex] = elements[i]; } }

Tenga en cuenta el Random generator opcional, si la implementación del marco base de Random no es segura para el subproceso o criptográficamente lo suficientemente fuerte para sus necesidades, puede inyectar su implementación en la operación.

En esta respuesta se puede encontrar una implementación adecuada para una implementación Random criptográficamente sólida segura para subprocesos.

Aquí hay una idea, extender IList de una manera (con suerte) eficiente.

public static IEnumerable<T> Shuffle<T>(this IList<T> list) { var choices = Enumerable.Range(0, list.Count).ToList(); var rng = new Random(); for(int n = choices.Count; n > 1; n--) { int k = rng.Next(n); yield return list[choices[k]]; choices.RemoveAt(k); } yield return list[choices[0]]; }


Mezcla cualquier lista (I)List con un método de extensión basado en la orden aleatoria de Fisher-Yates :

private static Random rng = new Random(); public static void Shuffle<T>(this IList<T> list) { int n = list.Count; while (n > 1) { n--; int k = rng.Next(n + 1); T value = list[k]; list[k] = list[n]; list[n] = value; } }

Uso:

List<Product> products = GetProducts(); products.Shuffle();

El código anterior utiliza el método System.Random muy criticado para seleccionar candidatos de intercambio. Es rápido pero no tan aleatorio como debería ser. Si necesita una mejor calidad de aleatoriedad en sus shuffles, use el generador de números aleatorios en System.Security.Cryptography así:

using System.Security.Cryptography; ... public static void Shuffle<T>(this IList<T> list) { RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider(); int n = list.Count; while (n > 1) { byte[] box = new byte[1]; do provider.GetBytes(box); while (!(box[0] < n * (Byte.MaxValue / n))); int k = (box[0] % n); n--; T value = list[k]; list[k] = list[n]; list[n] = value; } }

Una comparación simple está disponible en este blog (WayBack Machine).

Edit: desde que escribí esta respuesta hace un par de años, muchas personas me han comentado o escrito, para señalar la gran falla tonta en mi comparación. Por supuesto que tienen razón. No hay nada de malo en System.Random si se usa de la forma prevista. En mi primer ejemplo anterior, instalo la variable rng dentro del método Shuffle, que está pidiendo problemas si el método se llamará repetidamente. A continuación se muestra un ejemplo completo y fijo basado en un comentario realmente útil que recibimos hoy de @weston aquí en SO.

Program.cs:

using System; using System.Collections.Generic; using System.Threading; namespace SimpleLottery { class Program { private static void Main(string[] args) { var numbers = new List<int>(Enumerable.Range(1, 75)); numbers.Shuffle(); Console.WriteLine("The winning numbers are: {0}", string.Join(", ", numbers.GetRange(0, 5))); } } public static class ThreadSafeRandom { [ThreadStatic] private static Random Local; public static Random ThisThreadsRandom { get { return Local ?? (Local = new Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId))); } } } static class MyExtensions { public static void Shuffle<T>(this IList<T> list) { int n = list.Count; while (n > 1) { n--; int k = ThreadSafeRandom.ThisThreadsRandom.Next(n + 1); T value = list[k]; list[k] = list[n]; list[n] = value; } } } }


public static List<T> Randomize<T>(List<T> list) { List<T> randomizedList = new List<T>(); Random rnd = new Random(); while (list.Count > 0) { int index = rnd.Next(0, list.Count); //pick a random item from the master list randomizedList.Add(list[index]); //place it at the end of the randomized list list.RemoveAt(index); } return randomizedList; }


public Deck(IEnumerable<Card> initialCards) { cards = new List<Card>(initialCards); public void Shuffle() } { List<Card> NewCards = new List<Card>(); while (cards.Count > 0) { int CardToMove = random.Next(cards.Count); NewCards.Add(cards[CardToMove]); cards.RemoveAt(CardToMove); } cards = NewCards; } public IEnumerable<string> GetCardNames() { string[] CardNames = new string[cards.Count]; for (int i = 0; i < cards.Count; i++) CardNames[i] = cards[i].Name; return CardNames; } Deck deck1; Deck deck2; Random random = new Random(); public Form1() { InitializeComponent(); ResetDeck(1); ResetDeck(2); RedrawDeck(1); RedrawDeck(2); } private void ResetDeck(int deckNumber) { if (deckNumber == 1) { int numberOfCards = random.Next(1, 11); deck1 = new Deck(new Card[] { }); for (int i = 0; i < numberOfCards; i++) deck1.Add(new Card((Suits)random.Next(4),(Values)random.Next(1, 14))); deck1.Sort(); } else deck2 = new Deck(); } private void reset1_Click(object sender, EventArgs e) { ResetDeck(1); RedrawDeck(1); } private void shuffle1_Click(object sender, EventArgs e) { deck1.Shuffle(); RedrawDeck(1); } private void moveToDeck1_Click(object sender, EventArgs e) { if (listBox2.SelectedIndex >= 0) if (deck2.Count > 0) { deck1.Add(deck2.Deal(listBox2.SelectedIndex)); } RedrawDeck(1); RedrawDeck(2); }