Come accedere all'elemento casuale nell'elenco?


233

Ho un ArrayList e devo essere in grado di fare clic su un pulsante e quindi selezionare casualmente una stringa dall'elenco e visualizzarla in una finestra di messaggio.

Come farei per fare questo?

Risposte:


404
  1. Crea un'istanza di Randomclasse da qualche parte. Si noti che è piuttosto importante non creare una nuova istanza ogni volta che è necessario un numero casuale. È necessario riutilizzare la vecchia istanza per ottenere l'uniformità nei numeri generati. Puoi avere un staticcampo da qualche parte (fai attenzione ai problemi di sicurezza del thread):

    static Random rnd = new Random();
  2. Chiedi Randomall'istanza di fornirti un numero casuale con il massimo del numero di elementi nel ArrayList:

    int r = rnd.Next(list.Count);
  3. Visualizza la stringa:

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

C'è un buon modo per modificarlo in modo che un numero non venga ripetuto? Diciamo che volevo mescolare un mazzo di carte selezionandone casualmente una alla volta, ma ovviamente non posso selezionare la stessa carta due volte.
AdamMc331,

7
@ McAdam331 Cerca l'algoritmo Fisher-Yates Shuffle: en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Mehrdad Afshari

2
Dovrebbe essere "rnd.Next (list.Count-1)" invece di "rnd.Next (list.Count)" per evitare l'accesso all'elemento max, che sarebbe uno oltre l'indice presumibilmente basato su 0?
B. Clay Shannon,

22
@ B.ClayShannon No. Il limite superiore nella Next(max)chiamata è esclusivo.
Mehrdad Afshari,

1
Che dire quando l'elenco è vuoto?
tsu1980,

137

Di solito uso questa piccola raccolta di metodi di estensione:

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

Per un elenco fortemente tipizzato, ciò ti consentirebbe di scrivere:

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

Se tutto ciò che hai è un ArrayList, puoi lanciarlo:

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

qual è la complessità di quelli? la natura pigra di IEnumerable significa che non è O (N)?
Dave Hillier,

17
Questa risposta riordina l'elenco ogni volta che scegli un numero casuale. Sarebbe molto più efficiente restituire un valore di indice casuale, specialmente per elenchi di grandi dimensioni. Usalo in PickRandom - return list[rnd.Next(list.Count)];
swax l'

Questo non mescola l'elenco originale, ma lo fa su un altro elenco che potrebbe non essere ancora buono per l'efficienza se l'elenco è abbastanza grande ..
nawfal

.OrderBy (.) Non crea un altro elenco - Crea un oggetto di tipo IEnumerable <T> che scorre l'elenco originale in modo ordinato.
Johan Tidén,

5
L'algoritmo di generazione GUID è imprevedibile ma non casuale. Valuta invece di tenere un'istanza Randomin stato statico.
Dai,

90

Tu puoi fare:

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

Bellissimo. IN ASP.NET MVC 4.5, utilizzando un elenco, ho dovuto modificarlo in: list.OrderBy (x => Guid.NewGuid ()). FirstOrDefault ();
Andy Brown,

3
Nella maggior parte dei casi non importa, ma questo è probabilmente molto più lento rispetto all'utilizzo di rnd.Next. OTOH funzionerà su IEnumerable <T>, non solo sugli elenchi.
pesce solubile

12
Non sono sicuro di quanto sia casuale. Le guide sono uniche, non casuali.
pomber

1
Penso che una versione migliore ed estesa di questa risposta e il commento di @ solublefish siano ben riassunti in questa risposta (più il mio commento ) a una domanda simile.
Neo,

23

Crea Randomun'istanza:

Random rnd = new Random();

Prendi una stringa casuale:

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

Ricorda però che se lo fai frequentemente dovresti riutilizzare l' Randomoggetto. Inseriscilo come campo statico nella classe in modo che sia inizializzato solo una volta e quindi accedilo.


20

O una semplice classe di estensione come questa:

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

Quindi chiama:

myList.RandomElement();

Funziona anche per array.

Eviterei di chiamare OrderBy()perché può essere costoso per le raccolte più grandi. Utilizzare raccolte indicizzate come List<T>o matrici per questo scopo.


3
Le matrici in .NET già implementano IListquindi il secondo sovraccarico non è necessario.
Dai,

3

Perchè 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
Sebbene questo frammento di codice possa risolvere la domanda, inclusa una spiegazione aiuta davvero a migliorare la qualità del tuo post. Ricorda che stai rispondendo alla domanda per i lettori in futuro e quelle persone potrebbero non conoscere i motivi del tuo suggerimento sul codice.
gunr2171

3
Direi che il maxValueparametro del metodo Nextdovrebbe essere solo un numero di elementi in un elenco, non meno uno, perché secondo una documentazione " maxValue è il limite superiore esclusivo del numero casuale ".
David Ferenczy Rogožan,

1

Sto usando questo ExtensionMethod per un po ':

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

Avevi dimenticato di aggiungere l'istanza della classe Random
bafsar il

1

Suggerirò un approccio diverso, se l'ordine degli elementi all'interno dell'elenco non è importante durante l'estrazione (e ogni elemento deve essere selezionato solo una volta), quindi invece di un Listpuoi usare un ConcurrentBagche è una raccolta sicura, non ordinata di thread di oggetti:

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

The EventHandler:

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

Il TryTaketenterà di estrarre un oggetto "random" della collezione non ordinata.


0

Avevo bisogno di più oggetti invece di uno solo. Quindi, ho scritto questo:

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 questo, puoi ottenere elementi quanti ne vuoi in modo casuale come questo:

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

var randomItemList = _allItems.GetSelectedRandom(10); 

0

Stampa casuale del nome del paese dal file JSON.
Modello:

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

implementaton:

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.