C++ 线性搜索与二进制搜索效率

C++ 线性搜索与二进制搜索效率,c++,algorithm,performance,search,binary-search,C++,Algorithm,Performance,Search,Binary Search,我目前正在研究不同的搜索算法,我制作了一个小程序,看看效率上的差异。二进制搜索应该比线性搜索快,但时间测量结果却相反。是我在代码中犯了一些错误还是这是特殊情况 #include <chrono> #include <unistd.h> using namespace std; const int n=1001; int a[n]; void assign() { for (int i=0; i<n; i++) { a[i]=i;

我目前正在研究不同的搜索算法,我制作了一个小程序,看看效率上的差异。二进制搜索应该比线性搜索快,但时间测量结果却相反。是我在代码中犯了一些错误还是这是特殊情况

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

using namespace std;

const int n=1001;
int a[n];

void assign() {
    for (int i=0; i<n; i++) {
        a[i]=i;
    }
}

void print() {
    for (int i=0; i<n; i++) {
        cout << a[i] << endl;
    }
}

bool find1 (int x) {
    for (int i=0; i<n; i++) {
        if (x==a[i]){
            return true;
        } 
    } return false;
}

bool binsearch(int x) {
    int l=0,m; 
    int r=n-1;
    while (l<=r) {
        m = ((l+r)/2);
        if (a[m]==x) return true;
        if (a[m]<x) l=m+1;
        if (a[m]>x) r=m-1;

    }
    return false;
}

int main() {

    assign();
    //print();
    auto start1 = chrono::steady_clock::now();
    cout << binsearch(500) << endl;
    auto end1 = chrono::steady_clock::now();

    auto start2 = chrono::steady_clock::now();
    cout << find1(500) << endl;
    auto end2 = chrono::steady_clock::now();
    cout << "binsearch: " << chrono::duration_cast<chrono::nanoseconds>(end1 - start1).count()
        << " ns " << endl;
    cout << "linsearch: " << chrono::duration_cast<chrono::nanoseconds>(end2 - start2).count()
        << " ns " << endl;

    return 0;
}
#包括
#包括
使用名称空间std;
常数int n=1001;
int a[n];
无效分配(){

对于(int i=0;i您的测试数据集太小(1001个整数)。当您填充它时,它将完全适合最快的(一级)缓存;因此,您受到分支复杂性的约束,而不是内存。 二进制搜索版本显示出更多的分支预测失误,导致比简单线性传递更多的管道暂停

我将
n
增加到
1000001
,并增加了测试通过次数:

auto start1 = chrono::steady_clock::now();
for (int i = 0; i < n; i += n/13) {
    if (!binsearch(i%n)) {
        std::cerr << i << std::endl;
    }
}
auto end1 = chrono::steady_clock::now();

auto start2 = chrono::steady_clock::now();
for (int i = 0; i < n; i += n / 13) {
    if (!find1(i%n)) {
        std::cerr << i << std::endl;
    }
}
auto end2 = chrono::steady_clock::now();


还要注意的是,您不应该在定时循环中调用
cout
,但您确实需要使用查找结果,以使其不被优化。

在我看来,N=1001足以说明二进制搜索具有更好的性能。仅对于较小的N(大约<100),可能会更快。但是,在您的情况下,出现这种奇怪结果的原因是不正确的评测测量。在第一个算法(二进制搜索)的计算过程中,您的所有数据都已成功缓存,这大大提高了第二个算法(线性搜索)的性能

如果您只是交换他们的通话,您将得到相反的结果:

binsearch: 6019 ns 
linsearch: 77587 ns 
对于精确的测量,您应该使用特殊的框架(例如),以确保两种算法的“公平条件”

Other online(它在许多AWS机器上运行测试代码,这些机器的负载未知,并返回平均结果)为您的代码提供了这些图表,没有任何更改(同样的n=1001):

两者兼得

做一个二进制搜索到某个级别,然后切换到线性。这样想,二进制搜索有一大堆簿记;线性搜索更快,因为它“更简单”

当我第一次在汇编语言中进行这种实验时(早在20世纪70年代),我推断进行二进制搜索到大约4个项目,然后进行线性搜索,大约是最优的。然而,这取决于硬件,比较两个项目的复杂性(float/int/string/随便什么),等等


提示:计算代码中的操作数。我发现在binsearch()中,每个步骤所需的操作数大约是原来的两倍常规扫描与线性扫描。

TL;在足够小的子集上,由于内存位置的原因,二进制搜索可能比线性搜索慢。请尝试n=1000万。您是否记得在启用优化的情况下编译代码?您忘记了所有这些复杂度声明的引入语句:“存在一个
n_0
,因此对于所有
n>n_0
…”。注意
if(a[m]>x)
是不必要的。你已经知道它不是相等的,也不是更小的。所以它必须更大。将该行更改为
否则r=m-1;
重用缓存数据是有意义的。我没有考虑过这一点。还有一件事我不明白。我选择了n=1001,我搜索500是出于一个原因。在binsearch函数中,m将等于500,这将只导致两个比较-一个用于while循环,第一个if将返回true,而在linsearch中,“for循环”将进行1000次比较(i但如果首先启动binsearch,则必须读取行“a[m]处的未缓存数据)==x',它的成本远远超过几次比较。无论如何,这是一个依赖于硬件/编译器的问题,不是一个精确的评测。平均而言,在众多函数调用中,您的见解似乎完全正确,只需查看图表:binsearch立即返回答案。
binsearch: 6019 ns 
linsearch: 77587 ns