【问题标题】:How to access random item in list?如何访问列表中的随机项目?
【发布时间】:2011-01-02 10:17:15
【问题描述】:

我有一个 ArrayList,我需要能够单击一个按钮,然后从该列表中随机挑选一个字符串并将其显示在消息框中。

我该怎么做呢?

【问题讨论】:

    标签: c# arrays string random


    【解决方案1】:
    1. 在某处创建Random 类的实例。请注意,不要在每次需要随机数时创建新实例,这一点非常重要。您应该重用旧实例以实现生成数字的一致性。你可以在某处有一个static 字段(注意线程安全问题):

      static Random rnd = new Random();
      
    2. Random实例给你一个随机数,其中ArrayList中的项目数最大:

      int r = rnd.Next(list.Count);
      
    3. 显示字符串:

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

    【讨论】:

    • @McAdam331 查找 Fisher-Yates Shuffle 算法:en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
    • 这应该是 "rnd.Next(list.Count-1)" 而不是 "rnd.Next(list.Count)" 以避免访问元素最大值,这将是可能超过 0-基于索引?
    • @B.ClayShannon 号。Next(max) 调用中的上限是独占的。
    • list为空怎么办?
    • 0,到 0 将是 0,因为下限包含“胜于”上限独占。您将得到一个 indexoutofbound 异常。 @tsu1980
    【解决方案2】:

    我通常使用这个扩展方法的小集合:

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

    对于强类型列表,这将允许您编写:

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

    如果你只有一个 ArrayList,你可以转换它:

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

    【讨论】:

    • 它们的复杂性是什么? IEnumerable 的惰性是否意味着它不是 O(N)?
    • 每次您选择随机数时,此答案都会重新排列列表。返回随机索引值会更有效,尤其是对于大型列表。在 PickRandom 中使用它 - return list[rnd.Next(list.Count)];
    • 这不会对原始列表进行洗牌,实际上它会在另一个列表上进行,如果列表足够大,这仍然可能不利于效率..
    • .OrderBy(.) 不会创建另一个列表 - 它会创建一个 IEnumerable 类型的对象,该对象以有序的方式遍历原始列表。
    • GUID 生成算法是不可预测的,但不是随机的。考虑将Random 的实例保持在静态状态。
    【解决方案3】:

    你可以这样做:

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

    【讨论】:

    • 美丽。在 ASP.NET MVC 4.5 中,使用列表,我不得不将其更改为:list.OrderBy(x => Guid.NewGuid()).FirstOrDefault();
    • 在大多数情况下这无关紧要,但这可能比使用 rnd.Next 慢得多。 OTOH,它将适用于 IEnumerable,而不仅仅是列表。
    • 不确定这有多随机。指南是唯一的,而不是随机的。
    • 我认为这个答案的更好和扩展版本和@solvablefish 的评论在this answer(加上my comment)中很好地总结了一个类似的问题。
    • Guid.NewGuid() 这是为每个项目生成随机 uniq 密钥,然后按此 uniq 密钥排序。所以,这意味着随机有序列表。
    【解决方案4】:

    或者像这样的简单扩展类:

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

    然后只需调用:

    myList.RandomElement();
    

    也适用于数组。

    我会避免打电话给OrderBy(),因为它对于大型收藏品来说可能很昂贵。为此,请使用 List&lt;T&gt; 之类的索引集合或数组。

    【讨论】:

    • .NET 中的数组已经实现了IList,因此不需要第二次重载。
    【解决方案5】:

    创建一个Random 实例:

    Random rnd = new Random();
    

    获取随机字符串:

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

    但请记住,如果您经常这样做,您应该重新使用 Random 对象。把它作为一个静态字段放在类中,这样它就只初始化一次然后访问它。

    【讨论】:

      【解决方案6】:

      为什么不:

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

      【讨论】:

      • this IList&lt;T&gt; list 更好。否则可以进行多次枚举。
      【解决方案7】:

      我会建议不同的方法,如果列表中项目的顺序在提取时并不重要(并且每个项目只能选择一次),那么您可以使用 ConcurrentBag 而不是 List是一个线程安全、无序的对象集合:

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

      事件处理程序:

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

      TryTake 将尝试从无序集合中提取“随机”对象。

      【讨论】:

        【解决方案8】:
        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());
        

        【讨论】:

        • 虽然这段代码 sn-p 可以解决问题,including an explanation 确实有助于提高您的帖子质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
        • 我会说,Next 方法的 maxValue 参数应该只是列表中的一些元素,而不是减一个,因为根据文档“maxValue 是随机数的唯一上界".
        【解决方案9】:

        我已经使用这个 ExtensionMethod 有一段时间了:

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

        【讨论】:

        • 你忘记添加随机类实例
        【解决方案10】:

        我需要更多的项目而不是一个。所以,我写了这个:

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

        有了这个,你可以像这样随机获取你想要多少个元素:

        var _allItems = new List<TModel>()
        {
            // ...
            // ...
            // ...
        }
        
        var randomItemList = _allItems.GetSelectedRandom(10); 
        

        【讨论】:

          【解决方案11】:

          从 JSON 文件中随机打印国家名称。
          型号:

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

          实施:

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

          【讨论】:

            【解决方案12】:

            为什么不[2]:

            public static T GetRandom<T>(this List<T> list)
            {
                 return list[(int)(DateTime.Now.Ticks%list.Count)];
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多