【原创】启发式查询

启发式查询|Cacl_Search

此算法为bzy原创,转载务必获得允许(QQ:1143710044)

从一个单调队列中查询一个数字的位置一直是一个困扰人们的问题。
这个问题一直有一个十分简易的方法叫做搜索
实现如下:

int normal_Search(int *line,int value,int ub){
    for(int i = 0;i <= ub ;i ++){
        if(line[i] == value)return i; 
    }
    return 0;
}


O(n)的算法很显然有些慢,在运算100’000数据量1000’000次直接bz(1000s),显然不能满足
人们日益增长的追求美好生活的追求,于是聪明的先祖想出了二分查找

二分查找|Bianry_Search


学过OI/ACM肯定学过这个算法,它在计算机领域广泛运用,所以关于它的描述就从略。
二分查找是一种将单调队列从中间分快的方法,是已知运用最广泛的算法之一,代码如下:

int Binary_search(int *line,int value,int ub){
    int down = 0,up = ub;
    while(1){
        int mid = (up + down)>>1;
        if (line[mid] == value)return mid;
        if (line[mid] >  value){
            up = mid - 1;
        }else {
            down = mid + 1;
        }
    }
    return 0;
}


看完代码,其精髓思想便一目了然, O(log2n) 的时间复杂度和极地的常数使得它速度极快,同普通搜索一样的数据量它用时仅为1.6 – 2.0秒.
但是本着精益求精的思想,bzy想出了一个算法可以在常数上胜过二分查找,这就是启发式查询

!!!重头戏登场

启发式查询|Cacl_Search


已知单调队列 { Linei },其长度为ubount其对首记做Line[ 1 ],其尾记做Line[ ub ],所搜索的值记做Value,由此,我们可以近似估计Line 是一个等差数列,即可估计Value的位置大概在:
estimate = 1+(ub-1) *(value-line[1]) / (line[ub]-line[1]);
当然这个式子估计的位置只是估计值,不一定正确,当我,们可以确定范围,用Line[ estimate ]与value比较,如果大则范围在1 – estimate之间,否则在estimate-ub之间,直到有一个正确的estimate出现则返回即可,
代码如下:

inline const int Cacl_Search(int *line,const int value,const int ub){
    int down = 0;register int up=ub;
    while(1){
        register int estimate = down + 
        (long long)(up - down) *
        (long long)(value -line[down]) / 
        (line[up] - line[down]);
        if(estimate == down) ++ estimate ;
        if(estimate == up  ) -- estimate ;
        if(line[estimate] == value)return estimate;
        (line[estimate] < value?down:up) = estimate;
    }
    return 0;
}


在常数优化下运行同样数据只需1.1 – 1.2s,显然比二分查找快了很多.
这个算法时间复杂度不好估计,但效率较高,在卡常数时有一定作用.

后记|RP_search


最后送给大家一个测试人品的算法:

    int down = 0,up = ub;
    srand(233);'种子随意,看人品
    while(1){
        if(up == down)return down;

        register int estimate = down + 
        (long long)(up - down) *
        (long long)(value -line[down]) / 
        (line[up] - line[down]);
        if(line[estimate] == value)return estimate;
        int mid = down + rand()*rand()%(up - down);
        if (line[mid] == value)return mid;
        if(line[mid]>value){
            up = mid - 1;
        }else {
            down = mid + 1;
        }
    }
    return 0;

此算法为bzy原创,转载务必获得允许(QQ:1143710044)

点赞