在字典中查找最小差异的键

时间:2015-06-06 09:51:21

标签: c# linq dictionary

说,我有这个集合,它是通用字典

var items = new Dictionary<int, SomeData>
{
    { 1  , new SomeData() },
    { 5  , new SomeData() },
    { 23 , new SomeData() },
    { 22 , new SomeData() },
    { 2  , new SomeData() },
    { 7  , new SomeData() },
    { 59 , new SomeData() }
}

在这种情况下,键之间的最小距离(差异)= 1,例如,在23到22之间或在1到2之间

23 - 22 = 1 or 2 - 1 = 1

问题:如何找到通用字典中键之间的最小差异?是否有一行LINQ解决方案?

目的:如果有多个匹配,那么我只需要一个 - 最小的,这需要填充项之间缺少的键(间隙)

5 个答案:

答案 0 :(得分:3)

我不知道如何在LINQ中使用一行来完成它,但这是针对此问题的多行解决方案。

         var items = new Dictionary<int, string>();
         items.Add(1, "SomeData");
         items.Add(5, "SomeData");
         items.Add(23, "SomeData");
         items.Add(22, "SomeData");
         items.Add(2, "SomeData");
         items.Add(7, "SomeData");
         items.Add(59, "SomeData"); 

         var sortedArray = items.Keys.OrderBy(x => x).ToArray();

         int minDistance = int.MaxValue;

         for (int i = 1; i < sortedArray.Length; i++)
         {
             var distance = Math.Abs(sortedArray[i] - sortedArray[i - 1]);
             if (distance < minDistance)
                 minDistance = distance;
         }

         Console.WriteLine(minDistance);

答案 1 :(得分:2)

不确定Linq是否最合适但是(粗略地)这应该有效:

var smallestDiff = (from key1 in items.Keys
                    from key2 in items.Keys
                    where key1 != key2
                    group new { key1, key2 } by Math.Abs (key1 - key2) into grp
                    orderby grp.Key
                    from keyPair in grp
                    orderby keyPair.key1
                    select keyPair).FirstOrDefault ();

答案 2 :(得分:2)

我不会给你一个LinQ查询,因为已经有了答案。 我知道这不是你要求的,但我想告诉你如何以非常快速和易于理解/维护的方式解决它,如果性能和易读性与你有任何关系。

int[] keys;
int i, d, min;

keys = items.Keys.ToArray();
Array.Sort(keys); // leverage fastest possible implementation of sort

min = int.MaxValue;
for (i = 0; i < keys.Length - 1; i++)
{
  d = keys[i + 1] - key[i]; // d is always non-negative after sort
  if (d < min)
  {
    if (d == 2)
    {
      return 2; // minimum 1-gap already reached
    } else if (d > 2) // ignore non-gap
    {
      min = d;
    }
  }
}

return min; // min contains the minimum difference between keys

因为只有一种,这种非LinQ解决方案的性能执行得非常快。 我不是说这是最好的方式,但只是你应该测量两种解决方案并比较性能。

编辑:根据您的目的,我添加了这篇文章:

    if (d == 2)
    {
      return 2; // minimum 1-gap already reached
    } else if (d > 2) // ignore non-gap
    {
      min = d;
    }

现在这是什么意思?

假设有1个差距的概率很高,如果达到最小差距,则检查min的每次更改可能会更快。基于概率,当你通过for循环为1%或10%时,可能会发生这种情况。因此,对于非常大的集合(例如,超过100万或10亿)并且一旦您知道预期的概率,这种概率方法可能会为您带来巨大的性能提升。

相反,对于小集或当1间隙的概率很低时,这些额外的CPU周期会被浪费掉,如果没有这个检查,你会更好。

与非常大的数据库(想想概率索引)一样,概率推理变得相关。

问题在于你必须事先估计概率效应是否以及何时开始,这是一个相当复杂的话题。

编辑2: 1-gap实际上的索引差异为2。此外,1的索引差异是非差距(在两者之间插入索引没有间隙)。

所以之前的解决方案是错误的,因为只要两个索引是连续的(比如34,35),最小值就是1,这根本不是差距。

由于这个差距问题,内部if()是必要的,此时概率方法的开销无效。使用正确的代码和概率方法会更好!

答案 3 :(得分:1)

我认为LINQ最简单

首先,从词典中制作差异对

var allPair = items.SelectMany((l) => items.Select((r) => new {l,r}).Where((pair) => l.Key != r.Key));

然后找到差异的最小值

allPair.OrderBy((pair) => Math.Abs(pair.l.Key - pair.r.Key)).FirstOrDefault();

但是您可能有多个具有相同差异值的对,因此您可能需要在使用OrderBy之前使用GroupBy然后自己处理多对

答案 4 :(得分:1)

答案中未列出的单行解决方案:

items.Keys.OrderBy(x => x).Select(x => new { CurVal = x, MinDist = int.MaxValue }).Aggregate((ag, x) => new { CurVal = x.CurVal, MinDist = Math.Min(ag.MinDist, x.CurVal - ag.CurVal) }).MinDist