【问题标题】:Data structure to quickly find nearest floating point values快速查找最近浮点值的数据结构
【发布时间】:2014-07-08 17:19:52
【问题描述】:

如果我有以下一堆数据,我应该使用什么样的数据结构:

MyData
{
    float ValueA;
    float ValueB;
    object OtherInfo;
}

而且我需要快速找到所有满足这种条件的MyData 实例:

0.5 <= ValueA <= 0.7    AND    0.2 <= ValueB <= 0.9

找到这些项目很简单,但我需要尽快完成。是否有适合此的数据结构?我不介意用完额外的内存。

我目前的想法是有一个排序列表,按 ValueA 排序。然后我会进行二分搜索以找到 ValueA >= 0.5 的第一个项目,并迭代直到我得到一个 ValueA > 0.7 的项目。对于每次迭代,我检查 ValueB 是否介于 0.2 和 0.9 之间。我将所有匹配项放入我的输出数据集中。

有没有更好/更快的方法?

顺便说一句,我正在使用 C#。

【问题讨论】:

  • 我不认为二进制搜索会很好。它会找到一个 MyData.ValueA >= .5 的实例,但是您必须向后循环遍历集合才能找到 ValueA >= .5 的第一个实例
  • @Alex 一旦你找到了一个实例,当你只是向上或向下走时,找到所有其他符合条件的项目是一项恒定的工作,这是一件非常有效的事情去做。二分搜索正是适合这里工作的工具。
  • @Servy 搜索树以某种形式确实是可能的答案,但我认为除非我正在阅读“常量”,否则“查找所有其他项目的大量工作”是不可能的金额”错误 - 即,如果所有项目都符合条件并且您需要全部枚举它们怎么办?
  • @AlexeiLevenkov 这是一个恒定的数量每个匹配的项目。此方法不可能比 O(M) 执行得更快,其中 M 是匹配数。该方法的渐近复杂度永远不会比输出的大小更好,因为仅仅打印输出就需要这么多时间。
  • @Servy 确实有道理......我并不清楚你在原始评论中的意思。

标签: c# database optimization data-structures


【解决方案1】:

interval tree 将满足您的需求。示例实现(我尚未测试)at Codeplex。更多here.

更新:区间树与相关结构段树的比较在这里:What are the differences between segment trees, interval trees, binary indexed trees and range trees?

【讨论】:

    【解决方案2】:

    您的问题是二维的,但您只是在一维优化搜索空间。 quadtree 是一个合适的数据结构;在 C# 中找到合适的实现应该没有困难。如果您想要其他替代方案,则只需搜索 2D 空间分区算法。这基本上就是你在这里所做的; ValueA 和 ValueB 可能被视为 2D 坐标,即使这不是它们实际表示的内容。

    【讨论】:

    • 我认为四叉树不适用于此处。 terence 有一个实数,并希望找到包含它的所有区间。如果 terence 有两个实数,并且想要找到与这两个数字相似的开始和结束的其他区间,则四叉树将适用。
    • dbc,这不是我阅读他的问题的方式。他说他有多个包含 ValueA 和 ValueB 的数据元素,他想用单个间隔查询它们。这正是四叉树的优点。如果这不是他想要的,那么应该澄清最初的问题。
    【解决方案3】:

    您可以使用首先在 ValueA 上排序然后在 ValueB 上排序的单个列表来执行此操作。所以如果你有这些物品:

      A    B
    {0.5, 0.7}
    {0.3, 0.1}
    {0.5, 0.2}
    {0.5, 0.4}
    {0.2, 0.3}
    

    您可以像这样在列表中对它们进行排序:

    {0.2, 0.3}
    {0.3, 0.1}
    {0.5, 0.2}
    {0.5, 0.4}
    {0.5, 0.7}
    

    在 LINQ 中按两个标准排序非常简单:

    var sortedList = theList.OrderBy(s => s.ValueA).ThenBy(s => s.ValueB).ToList();
    

    您还可以通过将自定义比较委托传递给 Sort 方法来对列表进行就地排序。

    现在,根据搜索:0.5 &lt;= ValueA &lt;= 0.7 &amp;&amp; 0.2 &lt;= ValueB &lt;= 0.9,您可以执行以下操作:

    1. 二分法查找第一个 >= 0.5 的 ValueA。打电话给startA
    2. 二分法查找最后一个 endA
    3. 在区间[startA..endA] 中对第一个>= 0.2 的ValueB 进行二进制搜索。打电话给startB
    4. 在区间[startA..endA] 中对最后一个endB。

    您要查找的项目在区间[startB..endB]

    请注意,如果您要遍历 [startB..endB] 范围内的所有项目,则不必执行最后的二进制搜索,因为您可以在迭代期间将它们过滤掉。

    【讨论】:

      【解决方案4】:

      在此解决方案中,我将数据分箱到列表中,其中每个列表包含 widthX x widthY 框中的所有数据点。如果您的数据在 valA=[0,1], valB=[0,2] 范围内,并且您将网格划分为 0.1 x 0.1 的 bin(如代码所示),则 searchList(4,12) 是一个包含所有值的列表,例如 .4 &lt;= valA &lt;= .51.2 &lt;= valB &lt;= 1.3

      此解决方案仅对相当紧凑且相当大的输入数组有效。如果有任何极端异常值,您将创建许多不必要的垃圾箱。

      如果查询位于“自然”(即 .1、.01)边界上,则效果最佳,但这不是必需的。您必须手动循环遍历 searchList(i,j) 以过滤掉不需要的元素。

      搜索查询相当容易 - 在每个维度上找到您的最小/最大 bin,并遍历其间的所有 bin。

      static List<MyData>[,] GetSearchArray(List<MyData> srcList)
      {
        float minA = srcList.Min(s => s.valA);
        float minB = srcList.Min(s => s.valB);
        float maxA = srcList.Max(s => s.valA);
        float maxB = srcList.Max(s => s.valB);
      
        // Round the min's down, the max's up. 
        // If your searches are likely to fall on .1 intervals, that's a good rounding distance.
        // Note, they don't have to be the same for valA and valB
        minA = (float)Math.Floor(minA*10f)/10f;
        minB = (float)Math.Floor(minB*10f)/10f;
        maxA = (float)Math.Ceiling(maxA*10f)/10f;
        maxB = (float)Math.Ceiling(maxB*10f)/10f;
      
        // How many groupings between minA->maxA and minB->maxB
        int divsA = (int)Math.Round((maxA-minA)/.1);
        int divsB = (int)Math.Round((maxB-minB)/.1);
      
        // 2d array of data lists.
        var searchList = new List<MyData>[divsA,divsB];
      
        for (int i = 0; i < divsA; i++)
        {
          for (int j = 0; j < divsB; j++)
          {
            // row i: (minA + i*.1) <= valA <= (minA + (i+1)*.1)
            // col j: (minB + j*.1) <= valB <= (minB + (j+1)*.1)
            searchList[i, j] = new List<MyData>();
            var query = srcList.Where(s => (minA + i * .1 <= s.valA) && (s.valA <= minA + (i + 1) * .1) &&
                                           (minB + j * .1 <= s.valB) && (s.valB <= minB + (j + 1) * .1));
            foreach (MyData d in query)
              searchList[i, j].Add(d);
      
            // You may want to sort searchList here for fast intra-bin queries.
          }
        }
      
        return searchList;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-11-05
        • 2014-06-23
        • 2011-04-09
        • 2011-04-07
        • 1970-01-01
        • 2011-05-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多