为了搜索一个非常大的数组,我正在考虑一个复杂度小于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-我有一个接近均匀分布的阵列。
我想问的是它真的比二进制搜索更好吗?在哪些情况下它会失败?最好的,平均和最差的情况复杂度是什么?
答案 0 :(得分:2)
您正在使用启发式方法来尝试加速排序。启发式就像一个猜测。它并不能保证是正确的 - 但如果启发式算法很好,可以在一般情况下加速算法。
启发式算法通常不会改善算法的最坏情况运行时间。也就是说 - 启发式的某些输入可能是错误的。
我可以看到你正在做的直觉吸引力 - 你是"搜索"离您认为目标可能的位置越近。
但是你正在做的事情有两个问题:
例如,假设您有以下数组。 y是你的目标,x是所有其他值:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxYxx
在二进制搜索中,您可以将空间分成两半,然后在前两个决策中将其分成两半:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxYxx
^ ^
经过两次决策后,您的32值数组将下降到8个值的搜索空间。但是假设你的启发式,在第二个选择之后你把分裂放在y之后?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxYxx
^ ^
在您做出第二个决定之后,您只是略微减少了搜索空间。通过添加此启发式,您可以将最坏情况下的运行时间减少到N - 因为有可能构造输入来欺骗您的启发式,以便每次都做出最糟糕的猜测。
这是因为你对字典中单词的分布有所了解。但是如果有人不保证列表中的单词 - 那么你就不能保证字典搜索更快 - 例如你可以收到所有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。