【问题标题】:C# creating a list of random unique integersC# 创建一个随机唯一整数列表
【发布时间】:2011-11-02 14:12:59
【问题描述】:

我需要创建一个包含十亿个整数的列表,并且它们都必须是唯一的。我还需要非常快地完成这项工作。

创建一个列表并一个一个地添加随机数并检查每个是否重复非常慢。

如果我只是用随机数填充一个列表而不检查它们是否重复然后使用 distinct().toList(),这似乎相当快。我重复这个直到没有更多的重复。但是,创建新列表所使用的额外内存并不是最优的。有没有办法获得 distinct() 的性能,但不是创建一个新列表,而是修改源列表?

【问题讨论】:

  • 您要创建整数的范围有多大?
  • 这是以前要求的 C 语言。投票率最高的答案(与接受的答案相反)有一些可以移植到 C# 的建议。 stackoverflow.com/questions/1608181/…
  • 您将这些整数存储在什么数据类型中?无论如何,一个 32 位整数只能容纳 21 亿(2,147,483,647),因此您真正的“随机”程度是有限度的。
  • @Darin 他们是否在 .net 4 中添加了该功能?在 .net 2 中,2GB 限制甚至适用于 64 位应用程序。 blogs.msdn.com/b/joshwil/archive/2005/08/10/450202.aspx
  • 您没有在问题中包含几乎足够的信息来提供解决方案。例如,您是否要求列表按随机顺序排列,还是仅包含随机整数? (“选六”式彩票随机选择六个不同的整数;顺序无关紧要。但是从列表中随机选择六首歌曲并播放它们时,顺序也应该是随机的。)整数的范围是多少?他们可以是消极的吗?它们可以比 Int32 中的更大吗?一个 Int64?

标签: c# list unique distinct


【解决方案1】:

整数是否需要在一定范围内?如果是这样,您可以创建一个数组或列表,其中包含该范围内的所有数字(例如从 1 到 1000000000)并打乱该列表。

【讨论】:

    【解决方案2】:

    我发现这是在保持随机性的同时最快的:

            Random rand = new Random();
            var ints = Enumerable.Range(0, numOfInts)
                                         .Select(i => new Tuple<int, int>(rand.Next(numOfInts), i))
                                         .OrderBy(i => i.Item1)
                                         .Select(i => i.Item2);
    

    ...基本上为每个 int 分配一个随机 id,然后按该 id 排序并选择 int 的结果列表。

    【讨论】:

      【解决方案3】:

      您可以在单独的HashSet&lt;int&gt; 中跟踪重复项:

      var set = new HashSet<int>();
      var nums = new List<int>();
      
      while(nums.Count < 1000000000) {
          int num;
          do {
              num = rand.NextInt();
          } while (!set.Contains(num));
          set.Add(num);
          list.Add(num);
      }
      

      您需要一个单独的List&lt;int&gt; 来存储数字,因为哈希集不会保留您的随机排序。

      【讨论】:

      • 当列表被填满时,这个速度会越来越慢。可能需要几个小时才能找到适合列表的最后一个数字。
      • @CodeCaster:是的。如果洗牌是一种选择,那是一个更好的选择。
      • 拥有两个包含所有整数的对象不是一种选择。
      • 内部循环将永远运行,因为 set 最初将为空并且永远不会包含 num。应该去掉“!”来自循环条件。
      【解决方案4】:

      从字面上理解这个问题(一个包含十亿个整数的列表,它们都必须是唯一的):

      Enumerable<int>.Range(0, 1000000000)
      

      但按照 CodeCaster 的回答,您可以同时创建列表并随机播放:

      var count = 1000000000;
      var list = new List<int>(count);
      var random = new Random();
      list.Add(0);
      for (var i = 1; i < count; i++)
      {
          var swap = random.Next(i - 1);
          list.Add(list[swap]);
          list[swap] = i;
      }
      

      【讨论】:

        【解决方案5】:

        如果您从中提取的可能整数的数量(例如因子 2)明显大于您想要的整数数量,您可以简单地使用 HashSet&lt;T&gt; 来检查重复项。

        List<int> GetUniqueRandoms(Random random, int count)
        {
          List<int> result = new List<int>(count);
          HashSet<int> set = new HashSet<int>(count);
          for(int i = 0; i < count; i++)
          {
            int num;
        
            do
            {
              num = random.NextInt();
            while(!set.Add(num));
        
            result.Add(num);
          }
          return result;
        }
        

        这会分配具有正确容量的集合,以避免在增长期间重新分配。由于您的收藏很大,这应该是一个很大的改进。

        你也可以使用Distinct一次:

        IEnumerable<int> RandomSequence(Random random)
        {
            while(true)
            {
              yield return random.NextInt();
            }
        }
        
        RandomSequence(rand).Distinct().Take(1000000000).ToList();
        

        但是对于这两种解决方案,您都需要为HashSet&lt;int&gt;List&lt;int&gt; 提供足够的内存。


        如果您从中提取的可能整数数量与您想要的整数数量一样大,您可以创建一个包含所有整数的数组,将它们打乱,最后切断您不感兴趣的整数。

        您可以使用Jon Skeet's shuffle implementation

        【讨论】:

        • 为什么?我没有说我想迭代哈希集。我只会用它来检查重复项。
        【解决方案6】:

        如果您以一种已排序但仍是随机的方式创建列表(例如将随机数添加到列表的最后一个元素作为下一个元素),然后使用 Fisher-Yates-Durstenfeld 对列表进行混洗,该怎么办?这将在整体上以线性时间执行,这几乎与列表生成一样好。但是,它可能有一些会影响分布的显着偏差。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-04-29
          • 2013-11-20
          • 1970-01-01
          • 2021-06-25
          • 2016-02-05
          • 2018-04-08
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多