启发式查询|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)