¿Cómo acceder al elemento aleatorio en la lista?


233

Tengo una ArrayList y necesito poder hacer clic en un botón y luego seleccionar aleatoriamente una cadena de esa lista y mostrarla en un cuadro de mensaje.

¿Cómo haría para hacer esto?

Respuestas:


404
  1. Crea una instancia de Randomclase en alguna parte. Tenga en cuenta que es bastante importante no crear una nueva instancia cada vez que necesite un número aleatorio. Debe reutilizar la instancia anterior para lograr uniformidad en los números generados. Puede tener un staticcampo en alguna parte (tenga cuidado con los problemas de seguridad del hilo):

    static Random rnd = new Random();
  2. Pídale a la Randominstancia que le dé un número aleatorio con el máximo de la cantidad de elementos en ArrayList:

    int r = rnd.Next(list.Count);
  3. Mostrar la cadena:

    MessageBox.Show((string)list[r]);

¿Hay alguna buena manera de modificar esto para que no se repita un número? Digamos que quería barajar un mazo de cartas seleccionando aleatoriamente una a la vez, pero obviamente no puedo seleccionar la misma carta dos veces.
AdamMc331

77
@ McAdam331 Consulte el algoritmo Shuffle de Fisher-Yates: en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Mehrdad Afshari

2
¿Debería ser "rnd.Next (list.Count-1)" en lugar de "rnd.Next (list.Count)" para evitar acceder al elemento max, que sería uno más allá del índice presumiblemente basado en 0?
B. Clay Shannon

22
@ B.ClayShannon No. El límite superior de la Next(max)llamada es exclusivo.
Mehrdad Afshari

1
¿Qué pasa cuando la lista está vacía?
tsu1980

137

Usualmente uso esta pequeña colección de métodos de extensión:

public static class EnumerableExtension
{
    public static T PickRandom<T>(this IEnumerable<T> source)
    {
        return source.PickRandom(1).Single();
    }

    public static IEnumerable<T> PickRandom<T>(this IEnumerable<T> source, int count)
    {
        return source.Shuffle().Take(count);
    }

    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
    {
        return source.OrderBy(x => Guid.NewGuid());
    }
}

Para una lista fuertemente tipada, esto le permitiría escribir:

var strings = new List<string>();
var randomString = strings.PickRandom();

Si todo lo que tiene es una ArrayList, puede lanzarla:

var strings = myArrayList.Cast<string>();

¿Cuál es la complejidad de esos? ¿La naturaleza perezosa de IEnumerable significa que no es O (N)?
Dave Hillier

17
Esta respuesta vuelve a barajar la lista cada vez que elige un número aleatorio. Sería mucho más eficiente devolver un valor de índice aleatorio, especialmente para listas grandes. Use esto en PickRandom - return list[rnd.Next(list.Count)];
swax

Ésto no barajar la lista original, lo hace en otra lista, de hecho, que todavía no puede ser bueno para la eficiencia si la lista es lo suficientemente grande ..
nawfal

.OrderBy (.) No crea otra lista: crea un objeto de tipo IEnumerable <T> que itera por la lista original de forma ordenada.
Johan Tidén

55
El algoritmo de generación de GUID es impredecible pero no aleatorio. Considere mantener una instancia de Randomen estado estático en su lugar.
Dai

90

Tu puedes hacer:

list.OrderBy(x => Guid.NewGuid()).FirstOrDefault()

Hermoso. EN ASP.NET MVC 4.5, al utilizar una lista, tuve que cambiar esto a: list.OrderBy (x => Guid.NewGuid ()). FirstOrDefault ();
Andy Brown

3
En la mayoría de los casos no importará, pero probablemente sea mucho más lento que usar rnd.Next. OTOH funcionará en IEnumerable <T>, no solo en listas.
pescado soluble

12
No estoy seguro de cuán aleatorio es eso. Las guías son únicas, no aleatorias.
pomber

1
Creo que una versión mejor y extendida de esta respuesta y el comentario de @ solublefish se resumen muy bien en esta respuesta (más mi comentario ) a una pregunta similar.
Neo

23

Crea una Randominstancia:

Random rnd = new Random();

Obtener una cadena aleatoria:

string s = arraylist[rnd.Next(arraylist.Count)];

Sin embargo, recuerde que si hace esto con frecuencia, debe reutilizar el Randomobjeto. Póngalo como campo estático en la clase para que se inicialice solo una vez y luego acceda a él.


20

O una clase de extensión simple como esta:

public static class CollectionExtension
{
    private static Random rng = new Random();

    public static T RandomElement<T>(this IList<T> list)
    {
        return list[rng.Next(list.Count)];
    }

    public static T RandomElement<T>(this T[] array)
    {
        return array[rng.Next(array.Length)];
    }
}

Entonces solo llame:

myList.RandomElement();

Funciona para matrices también.

Evitaría llamar OrderBy()ya que puede ser costoso para colecciones más grandes. Utilice colecciones indexadas como List<T>o matrices para este propósito.


3
Las matrices en .NET ya se implementan, IListpor lo que la segunda sobrecarga es innecesaria.
Dai

3

Por qué no:

public static T GetRandom<T>(this IEnumerable<T> list)
{
   return list.ElementAt(new Random(DateTime.Now.Millisecond).Next(list.Count()));
}

2
ArrayList ar = new ArrayList();
        ar.Add(1);
        ar.Add(5);
        ar.Add(25);
        ar.Add(37);
        ar.Add(6);
        ar.Add(11);
        ar.Add(35);
        Random r = new Random();
        int index = r.Next(0,ar.Count-1);
        MessageBox.Show(ar[index].ToString());

3
Si bien este fragmento de código puede resolver la pregunta, incluir una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo la pregunta para los lectores en el futuro, y que esas personas podrían no conocer los motivos de su sugerencia de código.
gunr2171

3
Yo diría que el maxValueparámetro del método Nextdebe ser solo un número de elementos en una lista, no menos uno, porque de acuerdo con una documentación " maxValue es el límite superior exclusivo del número aleatorio ".
David Ferenczy Rogožan

1

He estado usando este ExtensionMethod por un tiempo:

public static IEnumerable<T> GetRandom<T>(this IEnumerable<T> list, int count)
{
    if (count <= 0)
      yield break;
    var r = new Random();
    int limit = (count * 10);
    foreach (var item in list.OrderBy(x => r.Next(0, limit)).Take(count))
      yield return item;
}

Se había olvidado de agregar una instancia de clase aleatoria
bafsar el

1

Sugeriré un enfoque diferente, si el orden de los elementos dentro de la lista no es importante en la extracción (y cada elemento debe seleccionarse solo una vez), entonces, en lugar de uno List, puede usar uno ConcurrentBagque es una colección desordenada y segura de hilos objetos:

var bag = new ConcurrentBag<string>();
bag.Add("Foo");
bag.Add("Boo");
bag.Add("Zoo");

El manejador de eventos:

string result;
if (bag.TryTake(out result))
{
    MessageBox.Show(result);
}

El TryTakeintentará extraer un objeto "al azar" de la colección no ordenada.


0

Necesitaba más elementos en lugar de solo uno. Entonces, escribí esto:

public static TList GetSelectedRandom<TList>(this TList list, int count)
       where TList : IList, new()
{
    var r = new Random();
    var rList = new TList();
    while (count > 0 && list.Count > 0)
    {
        var n = r.Next(0, list.Count);
        var e = list[n];
        rList.Add(e);
        list.RemoveAt(n);
        count--;
    }

    return rList;
}

Con esto, puede obtener elementos cuántos desea de forma aleatoria como esta:

var _allItems = new List<TModel>()
{
    // ...
    // ...
    // ...
}

var randomItemList = _allItems.GetSelectedRandom(10); 

0

Imprimir al azar el nombre del país desde el archivo JSON.
Modelo:

public class Country
    {
        public string Name { get; set; }
        public string Code { get; set; }
    }

Implementación:

string filePath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, @"..\..\..\")) + @"Data\Country.json";
            string _countryJson = File.ReadAllText(filePath);
            var _country = JsonConvert.DeserializeObject<List<Country>>(_countryJson);


            int index = random.Next(_country.Count);
            Console.WriteLine(_country[index].Name);

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.