【问题标题】:Fastest equivalent of comparing all elements of array比较数组所有元素的最快等价物
【发布时间】:2021-06-06 04:12:01
【问题描述】:

什么是 C#/LINQ 中最快的等价物,可以像这样比较数组的所有元素组合,如果它们不在存储桶中,则将它们添加到存储桶中。又名。如何在 C# 中优化这段代码。

// pseudocode

List<T> elements = { .... }
HashSet<T> bucket = {}

foreach (T element in elements)

    foreach (var myelemenet in elements.Where(e => e.id != element.id))
    {
        if (!element.notInTheList)
        {
               _elementIsTheSame = element.Equals(myelement);

               if (_elementIsTheSame)
               {
                   // append element to a bucket
                   
                   if (!elementIsInTheBucket(bucket, element))
                   {
                       element.notInTheList = true;
                       addToBucket(bucket, element);     
                   }
                }
            }
      }
 }
 // takes about 150ms on a fast workstation with only 300 elements in the LIST!

bucket中元素的最终顺序很重要

【问题讨论】:

  • “桶”是什么意思?您是否尝试从 MoreLINQ 实现类似 DisctinctBy 的东西?
  • 一个数组,其中元素的顺序很重要。
  • “如果不是元素不在桶中,则添加” - 您可能需要修复一些命名
  • 你能把“桶内”测试移到外循环吗?在所有元素的循环中,您再次循环相同的元素:O(n^2)
  • 真的不清楚你想用这个伪代码做什么,我们也不会猜到。缓慢的部分显然是内部循环——肯定是Where(e =&gt; e.property != element.property),如果它包含线性搜索,最终是elementIsInTheBucket。但如果mylelement.property != element.property,我看不出element.Equals(myelement) 怎么可能是真的。

标签: c# performance linq optimization


【解决方案1】:
elements.GroupBy(x=>x).SelectMany(x=>x);

https://dotnetfiddle.net/yZ9JDp

之所以有效,是因为 GroupBy 保留了顺序。

请注意,这会将每个等价类的第一个元素放在首位。您的代码将第一个元素放在最后,并跳过只有一个元素的类。 可以使用 SelectMany 之前的 where 跳过仅包含单个元素的类。

elements.GroupBy(x=>x).Where(x=>x.Skip(1).Any()).SelectMany(x=>x);

最后获取第一个元素有点棘手,但我怀疑这是你的代码中的一个错误,所以我不会尝试写出来。

根据您使用结果的方式,您可能希望在最后抛出一个 ToList()。

【讨论】:

  • 作为旁注,我之前使用过 groupBy(x=>x) (根据覆盖的等于进行分组)。而且我还使用 SelectMany(x=>x) 来展平嵌套列表。我认为这是我第一次在同一行中完成这两项工作。
  • 与 GroupBy 保留顺序你的意思是“伪”代码和 groupBy 都会以相同的顺序将元素放入存储桶中吗?
  • 为什么需要 Skip(1) ?你能解释一下吗?
  • Skip(1).Any() 是一种写 Count() 大于 1 的方法,不会遍历整个集合。
  • 是的。上面将按照与您的伪代码相同的顺序返回元素,但每个类中的第一个元素不会放在最后。 - 实际订单请参见小提琴的链接。
【解决方案2】:

听起来你实际上是在追踪DistinctBy?可以用类似的东西来模拟:

var list = new List<MainType>();
var known = new HashSet<PropertyType>();
foreach (var item in source)
{
    if (known.Add(item.TheProperty))
        list.Add(item);
}

您现在有一个项目列表,仅当通过所选属性存在重复项时才排在第一位,并保持顺序。

【讨论】:

    【解决方案3】:

    如果意图是找到最快的解决方案,正如 StackOverflow 标题所暗示的那样,那么我会考虑使用 CSharp 的 Parallel.ForEach 来执行 map 和 reduce。

    例如:

    var resultsCache = new IRecord[_allRecords.Length];
    var resultsCount = 0;
    
    var parallelOptions = new ParallelOptions
    {
        MaxDegreeOfParallelism = 1 // use an appropriate value
    };
    
    Parallel.ForEach(
                     _allRecords,
                     parallelOptions,
              // Part1: initialize thread local storage
              () => { return new FilterMapReduceState(); },
    
              // Part2: define task to perform
              (record, parallelLoopState, index, results) =>
              {
                  if (_abortFilterOperation)
                  {
                      parallelLoopState.Break();
                  }
    
                  if (strategy.CanKeep(record))
                  {
                      resultsCache[index] = record;
                      results.Count++;
                  }
                  else
                  {
                      resultsCache[index] = null;
                  }
    
                  return results;
              },
              // Part3: merge the results
              (results) =>
              {
                  Interlocked.Add(ref resultsCount, results.Count);
              }
    );
    

    在哪里

    class FilterMapReduceState
    {
        public FilterMapReduceState()
        {
            this.Count = 0;
        }
        /// <summary>
        /// Represents the number of records that meet the search criteria.
        /// </summary>
        internal int Count { get; set; }
    }
    

    【讨论】:

      【解决方案4】:

      据我了解,您需要的是这些部件。

      var multipleIds=elements.GroupBy(x => x.id)
                    .Where(g => g.Count() > 1)
                    .Select(y => y.Key);
        var distinctIds=elements.Select(x=>x.id).Distinct();
        var distinctElements=elements.Where(x=>distinctIds.Contains(x.id));
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-12-25
        • 1970-01-01
        • 2015-02-23
        • 2013-03-18
        • 1970-01-01
        • 1970-01-01
        • 2011-01-13
        相关资源
        最近更新 更多