我有一个大小为n的数组(比如n = 1000000),其值单调不减。我有一套' k'关键值(比如k = {1,23,39,55,..})。假设键值已排序。我必须使用最少的比较数在大数组中找到这些键值的索引。如何使用二进制搜索来搜索多个唯一值?对每个键值单独执行操作需要进行大量比较。当我在同一个大阵列上搜索另一个元素时,我能否以某种方式重复使用我在一次搜索中学到的知识?
答案 0 :(得分:5)
{0, len(haystack)}
初始化每对。这些对代表了我们对针的可能位置的所有知识。当你在大海捞针中有重复的值时,这里可能会有一些轻微的复杂情况,但我认为一旦你完成其余的整理,这应该不会太困难。
如果NumPy实现了这样的话,我很好奇。你正在做的事情的Python名称是numpy.searchsorted()
,一旦你通过API层,它就会出现在this:
/*
* Updating only one of the indices based on the previous key
* gives the search a big boost when keys are sorted, but slightly
* slows down things for purely random ones.
*/
if (@TYPE@_LT(last_key_val, key_val)) {
max_idx = arr_len;
}
else {
min_idx = 0;
max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
}
所以他们没有像我描述的那样进行全面的优化,但他们确实跟踪当前针头是否比最后一根针头更大,他们可以避免搜索最后一根针头被发现的下方的草垛。这是对简单实现的简单而优雅的改进,从评论中可以看出,它必须保持简单快速,因为该功能不需要首先对针进行排序。
顺便说一下:我提出的解决方案的目的是在大O方面达到理论上的最优性,但如果你有大量的针头,最快的事情就是对针头进行分类,然后在整个草堆上进行迭代。串联的所有针:线性搜索第一个针,然后从那里继续寻找第二个,等等。你甚至可以通过识别针是否大于A且小于C来跳过大海捞针中的每一个项目,它必须属于B位置(假设您不关心不在大海捞针中的针的左/右插入顺序)。然后你可以做len(haystack)/ 2比较,整个事情将非常适合缓存(当然,在排序针之后)。
答案 1 :(得分:4)
重用以前步骤中的知识的一种方法就像其他人建议的那样:一旦找到了某个键,就可以限制较小和较大键的搜索范围。
假设N = 2 ^ n,K = 2 ^ k且幸运结果: 找到中间键(n比较)后,你有两个大小为N / 2的子阵列。执行2次搜索“四分位”键(每次n-1比较),减少到N / 4个子阵列...
总计,n + 2(n-1)+ 4(n-2)+ ... + 2 ^(k-1)(n-k + 1)比较。经过一些数学计算,这大致等于K.n-K.k = K.(n-k)。
这是一个最好的案例,与独立搜索(K.n比较)相比,节省的费用并不那么显着。无论如何,最糟糕的情况(导致分区不平衡的所有搜索)并不比独立搜索差。
更新:这是最低比较合并问题的实例
在N个值的数组中查找K键的位置与合并两个已排序的序列相同。
来自Knuth Vol。 3,第5.3.2节,我们知道至少需要进行ceiling(lg(C(N+K,K)))
次比较(因为有C(N+K,K)
种方式来散布数组中的键。当K远小于N时,它接近lg((N^K/K!)
或K lg(N) - K lg(K) = K.(n-k)
。
这个边界不能被任何基于比较的方法打败,所以任何这样的算法都需要花费时间与键的数量基本成比例。
答案 2 :(得分:2)
虽然不是最佳,但实施起来要容易得多。
答案 3 :(得分:1)
如果你有一组整数,并且你想搜索最少的比较数,我想建议你从Knuth,6.2.1进行插值搜索。如果二进制搜索需要Log(N)次迭代(和比较),则插值搜索只需要Log(Log(N))操作。
有关详细信息和代码示例,请参阅:
答案 4 :(得分:0)
我知道问题是关于C,但我只是在Javascript中实现了这个,我以为我会分享。如果你在数组中有重复的元素,则无意工作...我认为在这种情况下它只会返回任何可能的索引。对于包含100万个元素的数组,您可以在其中搜索每个元素,其速度提高约2.5倍。如果您还搜索未包含在数组中的元素,那么它甚至更快。在一个数据集中,我通过它的速度要快几倍。对于小阵列,它大致相同
singleSearch=function(array, num) {
return this.singleSearch_(array, num, 0, array.length)
}
singleSearch_=function(array, num, left, right){
while (left < right) {
var middle =(left + right) >> 1;
var midValue = array[middle];
if (num > midValue) {
left = middle + 1;
} else {
right = middle;
}
}
return left;
};
multiSearch=function(array, nums) {
var numsLength=nums.length;
var results=new Int32Array(numsLength);
this.multiSearch_(array, nums, 0, array.length, 0, numsLength, results);
return results;
};
multiSearch_=function(array, nums, left, right, numsLeft, numsRight, results) {
var middle = (left + right) >> 1;
var midValue = array[middle];
var numsMiddle = this.singleSearch_(nums, midValue, numsLeft, numsRight);
if ((numsRight - numsLeft) > 1) {
if (middle + 1 < right) {
var newLeft = middle;
var newRight = middle;
if ((numsRight - numsMiddle) > 0) {
this.multiSearch_(array, nums, newLeft, right, numsMiddle, numsRight, results);
}
if (numsMiddle - numsLeft > 0) {
this.multiSearch_(array, nums, left, newRight, numsLeft, numsMiddle, results);
}
}
else {
for (var i = numsLeft; i < numsRight; i++) {
var result = this.singleSearch_(array, nums[i], left, right);
results[i] = result;
}
}
}
else {
var result = this.singleSearch_(array, nums[numsLeft], left, right);
results[numsLeft] = result;
};
}
答案 5 :(得分:-1)
//基于递归二进制搜索的函数。它返回x的索引 //给定数组arr [l..r]存在,否则为-1。
int binarySearch(int arr[], int l, int r, int x)
{
if (r >= l)
{
int mid = l + (r - l)/2;
// If the element is present at one of the middle 3 positions
if (arr[mid] == x) return mid;
if (mid > l && arr[mid-1] == x) return (mid - 1);
if (mid < r && arr[mid+1] == x) return (mid + 1);
// If element is smaller than mid, then it can only be present
// in left subarray
if (arr[mid] > x) return binarySearch(arr, l, mid-2, x);
// Else the element can only be present in right subarray
return binarySearch(arr, mid+2, r, x);
}
// We reach here when element is not present in array
return -1;
}