为什么线性搜索比二进制搜索快得多?

时间:2018-11-30 14:48:18

标签: c++ linux optimization

将以下代码考虑到数组中的find a peak

#include<iostream>
#include<chrono>
#include<unistd.h>


using namespace std;

//Linear search solution
int peak(int *A, int len)
{
    if(A[0] >= A[1])
        return 0;
    if(A[len-1] >= A[len-2])
        return len-1;

    for(int i=1; i < len-1; i=i+1) {
        if(A[i] >= A[i-1] && A[i] >= A[i+1])
            return i;
    }
    return -1;
}

int mean(int l, int r) {
    return l-1 + (r-l)/2;
}

//Recursive binary search solution
int peak_rec(int *A, int l, int r) 
{
    // cout << "Called with: " << l << ", " << r << endl;
    if(r == l)
        return l;
    if(r == l+ 1)
        return (A[l] >= A[l+1])?l:l+1;

    int m = mean(l, r);

    if(A[m] >= A[m-1] && A[m] >= A[m+1])
        return m;

    if(A[m-1] >= A[m])
        return peak_rec(A, l, m-1);
    else
        return peak_rec(A, m+1, r);
}


int main(int argc, char * argv[]) {
    int size = 100000000;
    int *A = new int[size];
    for(int l=0; l < size; l++)
        A[l] = l;

    chrono::steady_clock::time_point start = chrono::steady_clock::now();   
    int p = -1;
    for(int k=0; k <= size; k ++)
//      p = peak(A, size);
        p = peak_rec(A, 0, size-1);

    chrono::steady_clock::time_point end = chrono::steady_clock::now(); 

    chrono::duration<double> time_span = chrono::duration_cast<chrono::duration<double>>(end - start);

    cout << "Peak finding: " << p << ", time in secs: " << time_span.count() << endl;

    delete[] A;
    return 0;
}

如果我使用-O3进行编译并使用线性搜索解决方案(peak函数),则需要:

0.049 seconds

如果我使用的二进制搜索解决方案应该更快(peak_rec函数),则需要:

5.27 seconds

我尝试关闭优化,但这并没有改变情况。我也尝试了gcc和clang。

  

这是怎么回事?

2 个答案:

答案 0 :(得分:12)

这是在一个严格单调递增函数的情况下测试了它。线性搜索例程具有一个可检查最后两个条目的快捷方式,因此它甚至不会进行线性搜索。您应该测试随机数组以真正了解运行时的分布。

答案 1 :(得分:1)

之所以会发生这种情况,是因为您的线性搜索解决方案对排序数组进行了优化,就像您要传递给它一样。 if(A[len-1] >= A[len-2])会在您的数组排序上升时甚至在进入搜索循环之前返回您的函数,因此上升的排序数组的复杂性是恒定的。但是,您的二进制搜索将对数组进行完整搜索,因此需要更长的时间。解决方案是随机填充数组。您可以通过使用随机数生成器来实现:

int main() {
    std::random_device rd; /* Create a random device to seed our twisted mersenne generator */
    std::mt19937 gen(rd()); /* create a generator with a random seed */
    std::uniform_int_distribution<> range(0, 100000000); /* specify a range for the random values (choose whatever you want)*/
    int size = 100000000;
    int *A = new int[size];
    for(int l=0; l < size; l++)
        A[l] = range(gen); /* fill the array with random values in the range of 0 - 100000000
[ . . . ]

编辑:

当您随机填充数组时,一个非常重要的事情是:您的函数将无法与未排序的数组一起使用,因为如果第一个元素比第二个元素大,或者最后一个元素大于前一个,即使有一个两者之间的值更大。因此,如果您希望使用未排序的数组,请删除这些行(由于搜索峰值元素对于排序后的数组而言始终是恒定的复杂性,因此搜索没有意义)。