快速算法,用于搜索排序的浮点数组,以找到将输入值括起来的一对浮点


10

我有一个浮点数组,从最小到最大排序,并且需要能够选取大于或小于传递的输入值的最近浮点。此输入值不一定要作为数组中的值存在。

天真的方法是对数组进行简单的线性搜索。可能看起来像这样:

void FindClosestFloatsInArray( float input, std::vector<float> array, 
                               float *min_out, float *max_out )
{
    assert( input >= array[0] && input < array[ array.size()-1 ] );
    for( int i = 1; i < array.size(); i++ )
    {
        if ( array[i] >= input )
        {
            *min = array[i-1];
            *max = array[i];
        }
    }
}

但是很显然,随着数组变大,这将变得越来越慢。

是否有人对算法有一个想法,可以让我更好地找到这些数据?我已经切换到二进制搜索,它在某种程度上已改善了问题,但是它仍然比我想要的慢很多,并且由于我实际上并不是在寻找数组中存在的特定值,因此它永远不会终止早。

详细信息:数组中的浮点值不一定均匀分布(也就是说,数组可以包含值“ 1.f,2.f,3.f,4.f,100.f,1200.f ,1203.f,1400.f“。

我执行了数十万次此操作,但是如果可以改善查找时间,则可以对浮点数组进行任何数量的预处理。如果可以的话,我绝对可以更改为使用矢量以外的其他方式来存储它们。


是什么让您认为二进制搜索无法提前终止?当然,您可以只测试i和i + 1处的元素以查看它们是否在目标值中括起来,然后终止?
Paul R

或者,我可以测试i和i-1处的元素,以查看它们是否在目标值范围内。我还需要测试'i'是否> = array.size()-1,这样我才能避免进行您的测试,是否它<= 0,所以我可以避免进行测试...实际上很多在每个步骤都要执行的额外条件,以检查是否有提早执行的条件。我想他们会大大降低算法的速度,尽管我承认我还没有对此进行概要介绍。
Trevor Powell

3
不必太复杂-如果数组的大小为N,则只需将其视为大小为N-1。这样,在i + 1处总会有一个有效元素。在N-1个元素上对小于目标值的元素i进行二进制搜索,其中元素i + 1大于目标值。
Paul R

Answers:


11

正如您正确指出的那样,问题(线性搜索)中的代码对于大型浮点数组将变得缓慢。从技术上讲,它是O(n),其中n是数组中浮点值的数量。

通常,在有序数组中查找值的最佳方法是某种形式的递归树搜索(例如,二进制搜索),在这种情况下,您可以在元素数量上达到O(log n)查找时间在您的数组中。O(log n)的为比为O(n),更好地为n个大值。

因此,我建议的方法是对数组进行简单的二进制搜索,即:

  1. 设置最小/最大整数索引以覆盖整个浮点数组
  2. 针对搜索值x测试索引mid =(min + max / 2)在范围中间的值
  3. 如果x小于此值,则将max设置为mid,否则将min设置为mid
  4. 重复(2-4),直到找到正确的值

这是一个O(log n)算法,对于几乎所有情况都应该足够快。直观地讲,它是通过将每一步要搜索的范围减半直至找到正确的值来工作的。

简单的二进制搜索真的很困难,因此,如果您已经正确实现了此功能,那么您可能已经接近最佳状态了。但是,如果您知道数据的分布和/或查找值(x)的范围有限,那么您还可以尝试其他一些更高级的技巧:

  • 桶装 -创建桶(例如,对于两个整数之间的每个间隔),其中的每一个包含两个边界整数加上正下方和正上方的每个范围的两个值之间的浮点值的一个较小的排序列表。然后,您可以从(trunc(x)+0.5)开始搜索。如果选择适当大小的存储桶,这应该可以提高速度(这有效地增加了树的分支因子.....)。如果整数对您不起作用,则可以尝试使用其他一些定点精度的存储桶(例如1/16的倍数)。
  • 位映射 -如果可能的查找值范围足够小,则可以尝试创建一个由x的按位索引的大查找表。这将是O(1),但是您可能需要大量内存,这对您的缓存非常不友好...因此请谨慎使用。这特别令人讨厌,因为您正在查找浮点值,因此您可能需要几个GB才能解决所有不重要的位。
  • 舍入和散列 -散列表可能不是解决此问题的最佳数据结构,但是如果您可以在失去一点准确性的情况下幸免于难,那么它们就可以工作-只需舍入查找值的最低位并使用散列图直接查找正确的值。您将不得不尝试在哈希图大小和精度之间进行正确的权衡,并确保填充所有可能的哈希值,因此这可能有些棘手……
  • 树木平衡 -您理想的树木应该有50%的机会向左或向右走。因此,如果您基于查找值(x)的分布创建树,则可以优化树以使用最少的测试量来产生答案。如果您的float数组中的许多值非常接近,则这可能是一个很好的解决方案,因为它使您避免过于频繁地搜索这些分支。
  • 临界位树 -这些仍然是树(所以仍然是O(log n)...),但有些情况:但是,您需要将浮点数转换为某些定点格式才能进行比较

但是,除非您处于非常特殊的情况,否则我可能建议您坚持使用简单的二进制搜索。原因:

  • 实施起来容易得多
  • 在大多数情况下,速度非常快
  • 更复杂的方法的额外开销(例如更高的内存使用/缓存压力)通常超过了理论上的次要收益
  • 它将对未来数据分布的变化更加强大。

1

这似乎很简单:

对要绑定的浮点数进行二进制搜索-O(log n)时间。

然后,它左边的元素是下界,而它右边的元素是上界。


0

显而易见的答案是将花车存放在树上。在树中,支持“上一个”和“下一个”操作很简单。因此,只需对您的值做一个“下一个”,然后对您在第一步中找到的值做一个“上一个”。


1
这本质上与二进制搜索相同。
凯文·克莱恩

-1

本文(“无乘法的亚对数搜索”)可能会引起人们的兴趣。它甚至包含一些源代码。为了进行比较,可以将浮点数视为具有相同位模式的整数。这是IEEE浮点标准的设计目标之一。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.