【问题标题】:searching in a sorted array with less complexity than binary search在排序数组中搜索比二分搜索复杂度低
【发布时间】:2014-10-27 10:52:58
【问题描述】:

要搜索一个非常大的数组,我正在考虑一种复杂度小于 log n 的算法,这意味着顺序不小于 log n,但绝对小于 log n。所以我所做的不是直接走到中间向前移动 1 步,检查如果数字均匀分布,我们必须进一步移动多少,移动到那个位置,如果这是一个解决方案,打破它,否则计算我们必须进一步移动多少,迭代执行直到找到解决方案 这是一个有效的 Java 代码:-

 public class Search {
        public static void main(String[] args) {
            int a[]={12,15,16,17,19,20,26,27};
            int required=27;
            int pointer=0;
            int n=1;
            int diff;
            int count=0;
            int length=a.length;
            while(a[pointer]!=required){
                count++;
                if ((pointer+n)>(length-1))
                    n=length-1-pointer;
                if(n==0)
                    n=-1;
                diff=a[pointer+n]-a[pointer];
                pointer=pointer+n;
                n=(required-a[pointer])*n/diff;


            }
            System.out.println(pointer);
            System.out.println(count);
        }

    }

P.S- 我有一个接近均匀分布的数组。

我想问一下它真的比二分查找更好吗??在什么情况下它会失败?最好、平均和最差情况的复杂度是多少??

【问题讨论】:

  • 您的做法是个坏主意,几乎在所有情况下都会减慢您的搜索速度。
  • @Rafael 你能解释一下为什么吗?
  • 唯一比二分搜索更快的搜索是散列。 O(1) 复杂度。除此之外,就复杂性而言,二分搜索几乎是您所希望的最好的。
  • 1.如果您的数组接近线性分布,您可以通过线性插值估计二进制搜索的前几个 MSB 位。如果 N 足够大但不复杂,则可以改善运行时间。 2.您要实现的目标与 O(log(N)) 几乎相同,但开销更大,日志库也不同 2. 所以最终您的运行时可能会比原始二进制搜索更糟
  • 你正在重塑Interpolation Search。它可能会更快,因为它可能会执行更少的探测,但在实践中也可能会更慢,因为代码更复杂。

标签: algorithm sorting search


【解决方案1】:

您正在使用启发式方法来尝试加速排序。启发式方法就像一个猜测。不能保证它是正确的 - 但如果启发式方法很好,则可以在一般情况下加速算法。

启发式通常不会改善算法在最坏情况下的运行时间。也就是说 - 启发式的某些输入可能是错误的。

我可以看到您正在做的事情的直观吸引力 - 您正在“搜索”更接近您认为您的目标可能的位置。

但是你所做的有两个问题:

  1. 将二分搜索中的“拆分”移动到更靠近目标的位置不会加快搜索速度。在二进制搜索中,您每次将搜索空间分成两半。当您将分割点移近目标时,您还没有找到目标,并且您的目标很可能在两个不相等的空间中较大的一个。

例如,假设您有以下数组。 y 是您的目标,x 是所有其他值:

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxYxx

在二分搜索中,您会将空间分成两半,然后在前两个决定中再分成两半:

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxYxx
                ^        ^

经过两次决定,您的 32 值数组减少到 8 个值的搜索空间。但是假设用你的启发式方法,在第二个选择之后你把分裂放在 y 之后?

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxYxx
                ^             ^

在您做出第二个决定后,您只减少了一点搜索空间。通过添加此启发式,您可以将最坏情况的运行时间减少到 N - 因为可以构造输入,从而欺骗您的启发式每次都做出最坏的猜测。

  1. 另一个问题是,加速搜索的启发式方法只有在您对搜索内容有所了解时才会有所帮助。进行字典搜索。你知道 z 在字母表的末尾。所以当你得到一个以 z 开头的单词时,你就很清楚 z 单词在字典中的位置。您不必从字典的中间开始。

这是因为您对字典中单词的分布有所了解。但是,如果有人对列表中的单词不做任何保证——那么你就不能保证字典搜索会更快——例如,你可能会收到一个包含所有 z 个单词的列表。

在您的情况下,您的启发式方法并不是特别好。您正在猜测下一个拆分的位置是基于当前拆分与前一个值之间的距离。唯一可以很好猜测的情况是列表中的元素是否均匀分布。如果它们间隔不均匀(几乎总是),那么一些猜测总是会超过分割和其他下冲。

在任何不均匀数字的排序数组中,必然会有比平均间隔更紧密的间隔,以及比平均间隔更稀疏的间隔。您的启发式猜测当前拆分到数组末尾的数字的平均稀疏度。这两件事之间没有任何关系。

更新:

您的最佳案例时间:O(1) - 例如你猜对了索引。

最坏情况:O(N) - 例如每个选择都是最糟糕的。

您补充说您的阵列几乎均匀分布并且非常大。我对实践中最快的猜测是:查找数组中的第一个数字和最后一个数字,以及数组的长度。对目标的偏移量进行有根据的猜测:

offset = floor((( target - first ) / ( last - first )) * length );

在目标周围选择一个合理的搜索空间:

window_start = floor( offset * ( 1 - alpha ));
window_end   = floor( offset * ( 1 + alpha ));

对该窗口定义的子数组进行二分搜索。

您设置的 alpha 值取决于您认为数组的规则程度。例如。您可以设置为 0.05 以搜索大约占您估计目标周围总搜索空间 10% 的窗口。

如果您可以对输入的均匀性做出一些保证,您也许可以优化调整 alpha。

【讨论】:

  • 谢谢你这么好的解释。你的猜测算法很完美,但有一个问题。毫无疑问,我有一个非常大的数组,它几乎是均匀分布的,但在大多数情况下不是总是。所以不可能决定 alpha。因此,我认为我必须只将我的解决方案付诸实施。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多