【问题标题】:What is the most efficient way to compare multiple sets of data for overlaps in C#在 C# 中比较多组数据是否重叠的最有效方法是什么
【发布时间】:2015-03-19 08:53:54
【问题描述】:

我想知道在 C# 中查找具有不同记录数的多个集合中的重叠数据的最有效方法是什么?

让我们举个例子:

Set1: +- 20 records       Set2: +- 50 records
 ID | Value                ID | Value
 1  | Value01               1 | ValueA
 2  | Value02               2 | ValueB
 3  | Value03               3 | Value01
 4  | Value04               4 | ValueD
 5  | Value05               5 | Value17
   ...                           ....
20  | Value20              50 | Value XY


Set3: +- 2 000 records    Set4: +- 3 000 records
 ID | Value               ID | Value
 1  | Random               1 | Dog
 2  | Random02             2 | Duck
 3  | Random03             3 | John
 4  | Random04             4 | Pen
 5  | Ranodm05             5 | Argument
   ...                           ....

这个想法是检查,每个集合只包含独占记录。如果一个记录存在于多个表中,则该记录应标记为重叠。

数学上说:

∀A、B、C、D:A ∩ (B ∪ C ∪ D) = {} ∧ B ∩ (C ∪ D) = {} ∧ C ∩ D = {}

我想要实现的是在 C# 中创建一个函数,该函数将 4 个集合作为输入,并可能返回类似于字典的内容。包含3个字典(set1重叠,set2重叠,set3重叠)

所以输出应该是这样的:

var result = new Dictionary<SetsEnum, Dictionary<int, Dictionary<SetsEnum, int>>>
{
  { SetsEnum.Set1, 
    { 
      {<set1overlappingRowId1>,
        {
          { SetsEnum.<setX>, <overlappingRowIdX> },
          { SetsEnum.<setY>, <overlappingRowIdY> } 
        }
      },
      {<set1overlappingRowId2>,
        {
          { SetsEnum.<setZ>, <overlappingRowIdZ> }
        }
      }
    }
  }
}

我希望我没有在括号中犯任何错误。 基本上它应该这样说:

Set1-Row5 与:Set2-Row42、Set3-Row1513 重叠 Set1-Row18 与:Set4-Row481 重叠 Set2-Row30 与:Set3-Row987 重叠 等等

我希望它足够清楚。

我能想到的唯一方法 id:

  1. 按大小从小到大排列集
  2. 对于集合 1 中的每条记录,检查集合 2、3 和 4 中的重叠。
  3. 对于第 2 组中的每条记录,检查第 3 组和第 4 组中的重叠。
  4. 对于第 3 组中的每条记录,检查第 4 组中的重叠。
  5. 返回重叠字典。

在 C# 中是否存在类似这样的更简单的解决方案?

附言。事实上,我想知道另一种解决方案。如果发现任何重叠,用标志标记这条记录,然后只处理不重叠的记录(这可能会加快速度。但这种功能的实现似乎没有必要复杂)

【问题讨论】:

    标签: c# performance


    【解决方案1】:

    由于您正在执行一种内部连接,因此您可以在这里使用它作为起点 - 请注意,它会生成完整的外部连接:

    public static IEnumerable<TResult> GenerateMapping<TLeft, TRight, TResult>(IEnumerable<TLeft> leftList,
                                                                                    IEnumerable<TRight> rightList,
                                                                                    Func<TLeft, String> leftSortKey,
                                                                                    Func<TRight, String> rightSortKey,
                                                                                    Func<TLeft, TRight, TResult> factory)
            where TLeft : class
            where TRight : class
        {
            var result = new List<TResult>();
            var sortedLeftList = leftList.OrderBy(leftSortKey, StringComparer.OrdinalIgnoreCase).ToArray();
            var sortedRightList = rightList.OrderBy(rightSortKey, StringComparer.OrdinalIgnoreCase).ToArray();
    
            var left = 0;
            var right = 0;
            var total = sortedLeftList.Length + sortedRightList.Length;
    
            while (left + right < total)
            {
                if (left < sortedLeftList.Length && right < sortedRightList.Length)
                {
                    var compare = String.Compare(
                        leftSortKey(sortedLeftList[left]),
                        rightSortKey(sortedRightList[right]),
                        StringComparison.OrdinalIgnoreCase);
    
                    if (compare < 0)
                    {
                        result.Add(factory(sortedLeftList[left], null));
                        left++;
                    }
                    if (compare > 0)
                    {
                        result.Add(factory(null, sortedRightList[right]));
                        right++;
                    }
                    if (compare == 0)
                    {
                        result.Add(factory(sortedLeftList[left], sortedRightList[right]));
                        left++;
                        right++;
                    }
                }
                else if (left < sortedLeftList.Length)
                {
                    result.Add(factory(sortedLeftList[left], null));
                    left++;
                }
                else if (right < sortedRightList.Length)
                {
                    result.Add(factory(null, sortedRightList[right]));
                    right++;
                }
            }
    
            return result;
        }
    

    可能某些使用 Lookups 的实现会执行得更快。 您可以通过搜索 [inner|left|right|full] join 和 LinQ 找到它们

    【讨论】:

      【解决方案2】:

      如果您将所有集合连接成一个集合,然后按值排序或按值分组,您可以轻松计算每个值出现的次数。您需要跟踪每个项目的来源。
      在这个例子中,我使用了 3 个小集合和简单的整数和字符串,但思路应该很清楚。

      struct MyData
      {
          public int SetID { get; set; }
          public int ID { get; set; }
          public string Value { get; set; }
      
          public override string ToString()
          {
              return string.Format("SetID={0}, ID={1}, Value={2}", SetID, ID, Value);
          }
      }
      

      然后是搜索本身:

      var set1 = new Dictionary<int, string>();
      var set2 = new Dictionary<int, string>();
      var set3 = new Dictionary<int, string>();
      
      set1.Add(1, "Value01");
      set1.Add(2, "Value02");
      set1.Add(3, "Value03");
      set1.Add(4, "Value04");
      set1.Add(5, "Value05");
      set1.Add(6, "Value06");
      set1.Add(7, "Value07");
      set1.Add(8, "Value08");
      set1.Add(9, "Value09");
      set1.Add(10, "Value10");
      
      set2.Add(1, "ValueA");
      set2.Add(2, "ValueB");
      set2.Add(3, "Value01");
      set2.Add(4, "ValueD");
      set2.Add(5, "Value17");
      set2.Add(6, "ValueX");
      set2.Add(7, "ValueY");
      set2.Add(8, "ValueZ");
      set2.Add(9, "Value16");
      
      set3.Add(1, "ValueT");
      set3.Add(2, "Random");
      set3.Add(3, "Duck");
      set3.Add(4, "Arg");
      set3.Add(5, "Value03");
      set3.Add(6, "Value01");
      set3.Add(7, "ValueD");
      set3.Add(8, "ValueB");
      set3.Add(9, "Whatever");
      
      var search = set1.Select(kvp => new MyData { SetID = 1, ID = kvp.Key, Value = kvp.Value })
          .Concat(set2.Select(kvp => new MyData { SetID = 2, ID = kvp.Key, Value = kvp.Value })
      ).Concat(set3.Select(kvp => new MyData { SetID = 3, ID = kvp.Key, Value = kvp.Value })
      ).GroupBy(md => md.Value);
      
      var unique = new HashSet<MyData>();
      var dupes = new HashSet<MyData>();
      foreach (var grp in search) {
          if (grp.Take(2).Count() > 1) {
              foreach (var data in grp) dupes.Add(data);
          } else {
              unique.Add(grp.Single());
          }
      }
      foreach (var data in unique) Console.WriteLine(data);
      Console.WriteLine();
      foreach (var data in dupes) Console.WriteLine(data);
      

      欺骗将包含:

      SetID=1, ID=1, Value=Value01
      SetID=2, ID=3, Value=Value01
      SetID=3, ID=6, Value=Value01
      SetID=1, ID=3, Value=Value03
      SetID=3, ID=5, Value=Value03
      SetID=2, ID=2, Value=ValueB
      SetID=3, ID=8, Value=ValueB
      SetID=2, ID=4, Value=ValueD
      SetID=3, ID=7, Value=ValueD
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-06
        • 2015-05-12
        • 2016-05-26
        • 2011-05-08
        • 1970-01-01
        • 2011-03-17
        相关资源
        最近更新 更多