Performance 非均匀分布的二进制搜索

Performance 非均匀分布的二进制搜索,performance,algorithm,binary-search,Performance,Algorithm,Binary Search,对于均匀分布,二进制搜索是高效的。列表中的每个成员都有相同的“命中”概率。这就是为什么你每次都去中心 对于非均匀分布是否有有效的算法?e、 g.一个1/x分布之后的分布。让我精确一点。二进制搜索所需的是: Given array A which is sorted, but have non-uniform distribution Given left & right index L & R of search range Want to search for a val

对于均匀分布,二进制搜索是高效的。列表中的每个成员都有相同的“命中”概率。这就是为什么你每次都去中心


对于非均匀分布是否有有效的算法?e、 g.一个1/x分布之后的分布。

让我精确一点。二进制搜索所需的是:

 Given array A which is sorted, but have non-uniform distribution
 Given left & right index L & R of search range
 Want to search for a value X in A

 To apply binary search, we want to find the index M in [L,R] 
 as the next position to look at.

 Where the value X should have equal chances to be in either range [L,M-1] or [M+1,R]
一般来说,您当然希望在您认为X值应该在A中的位置选择M。 因为即使你错过了,总“机会”的一半也会被消除

所以在我看来,你对分销有一些期望。 如果你能告诉我们“1/x分布”的确切含义,那么 也许这里有人可以根据我的建议为你提供帮助


让我举一个有效的例子

我将使用与@Leonid Volnitsky类似的“1/x分布”解释

下面是生成输入数组
a

from random import uniform

# Generating input
a,b = 10,20
A = [ 1.0/uniform(a,b) for i in range(10) ]
A.sort()

# example input (rounded)
# A = [0.0513, 0.0552, 0.0562, 0.0574, 0.0576, 0.0602, 0.0616, 0.0721, 0.0728, 0.0880]
假设要搜索的值为:

X = 0.0553
那么X的估计指数为:

= total number of items * cummulative probability distribution up to X
= length(A) * P(x <= X)
因此

嗯,这是非常准确的

要重复预测X在A的每个给定部分中的位置的过程,需要更多的工作。但我希望这足以说明我的想法

我知道这个可能不再像是二进制搜索了 如果你能在一步之内得到接近的答案。
但是请承认,如果您知道输入数组的分布,这就是您可以做的。

二进制搜索的目的是,对于已排序的数组,每次将数组减半时,您都在最小化最坏情况,例如,您可以执行的最坏检查数是log2(条目)。如果您执行某种“不均匀”的二进制搜索,将数组分为更小和更大的一半,如果元素始终位于更大的一半,则可能会出现更糟糕的最坏情况。因此,我认为无论预期分布如何,二进制搜索仍然是最好的算法,因为它具有最好的最坏情况行为。

我将根据您的描述假设:

  • X是均匀分布的
  • Y=1/X
    是您要搜索的数据,它存储在排序表中
  • 给定值y,需要在上表中对其进行二进制搜索
二进制搜索通常使用范围中心的值(中值)。对于均匀分布,可以通过了解表中的大致位置来加快搜索速度,我们需要查找搜索值

例如,如果我们在
[0,1]
范围内有均匀分布的值,并且查询的是
0.25
,最好不要查看范围的中心,而是查看范围的第一季度


要对1/X数据使用相同的技术,请将数据存储在表中,而不是Y,而是反向1/Y。不要搜索y,而是搜索反向值1/y

您有一个条目向量,比如
[x1,x2,…,xN]
,您知道这样一个事实,即查询的分布是以概率
1/x
给出的。这意味着您的查询将使用该分布进行,即在每次咨询中,您将以更高的概率获取元素
xN

这会使二进制搜索树在考虑标签的情况下保持平衡,但不会强制执行任何搜索策略。该策略的一个可能变化是放宽平衡二叉搜索树的约束——父节点左侧较小,右侧较大——并实际选择父节点作为概率较高的节点,子节点作为两个最可能的元素

请注意,不是一个二叉搜索树,因为您并不是在每一步都将搜索空间除以二,而是一个关于搜索模式分布的重新平衡树。这意味着您最糟糕的搜索情况可能会达到
O(N)
。例如,有
v=[10,20,30,40,50,60]

        30
      /    \
    20      50
   /       /  \
 10       40   60
可以使用函数
f(x)=1/x来重新排序或重新平衡:

f([10, 20, 30, 40, 50, 60]) = [0.100, 0.050, 0.033, 0.025, 0.020, 0.016]
sort(v, f(v)) = [10, 20, 30, 40, 50, 60]
进入新的搜索树,如下所示:

        10  -------------> the most probable of being taken
      /    \               leaving v = [[20, 30], [40, 50, 60]]
    20      30  ---------> the most probable of being taken
           /  \            leaving v = [[40, 50], [60]]
          40   50 -------> the most probable of being taken
              /            leaving v = [[60]]
             60

如果您搜索
10
,您只需要一次比较,但是如果您正在搜索
60
,您将执行
O(N)
比较,这不符合二进制搜索的条件。正如@Steve314所指出的,离完全平衡的树越远,搜索的最差情况就越差。

二叉搜索和二叉树之间有着深刻的联系-二叉树基本上是一种“预先计算”的二叉搜索,其切点由树的结构决定,而不是在搜索运行时被选择。事实证明,处理每个密钥的概率“权重”有时是用二叉树完成的

一个原因是因为它是一个相当普通的二叉搜索树,但事先就知道了,完全了解查询概率

Niklaus Wirth在他的《算法和数据结构》一书中介绍了这一点,其中有几个变体(一个用于Pascal,一个用于Modula 2,一个用于Oberon),至少有一个可从他的网站下载

然而,二叉树并不总是二叉搜索树,二叉树的一个用途是派生一个搜索树

无论哪种方法,二叉树都是从分开的叶子开始构建的,在每一步,将两个最不可能的子树合并成一个更大的子树,直到只剩下一个子树。为了在每个步骤中有效地选择两个最不可能的子树,使用了优先级队列数据结构—可能是一个

一个二叉树只要构建一次就永远不会被修改,它可以有很多用途,但是一个可以有效更新的二叉树更有用。有一些权重平衡的二叉树数据结构,但我不熟悉。当心——术语“体重”
        30
      /    \
    20      50
   /       /  \
 10       40   60
f([10, 20, 30, 40, 50, 60]) = [0.100, 0.050, 0.033, 0.025, 0.020, 0.016]
sort(v, f(v)) = [10, 20, 30, 40, 50, 60]
        10  -------------> the most probable of being taken
      /    \               leaving v = [[20, 30], [40, 50, 60]]
    20      30  ---------> the most probable of being taken
           /  \            leaving v = [[40, 50], [60]]
          40   50 -------> the most probable of being taken
              /            leaving v = [[60]]
             60