[英]Binary search for multiple distinct numbers in a large array in minimum number of comparisons
我有一个大的n数组(比如n = 1000000),其值单调不减。 我有一组'k'键值(比如k = {1,23,39,55,..})。 假设键值已排序。 我必须使用最少的比较数在大数组中找到这些键值的索引。 如何使用二进制搜索来搜索多个唯一值? 对每个键值单独执行操作需要进行大量比较。 当我在同一个大阵列上搜索另一个元素时,我能否以某种方式使用我在一次搜索中学到的知识?
{0, len(haystack)}
初始化每对。 这些对代表了我们对针的可能位置的所有知识。 当你在大海捞针中有重复的值时,这里可能会有一些轻微的复杂情况,但我认为一旦你完成了其余的整理,这应该不会太困难。
如果NumPy实现了这样的话,我很好奇。 Python的名字,你正在做的是什么numpy.searchsorted()
一旦你通过API层得到谈到这个 :
/*
* 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比较,整个事情将非常缓存(当然,在排序针之后)。
重用以前步骤中的知识的一种方法是像其他人建议的那样:一旦找到了键,就可以限制较小和较大键的搜索范围。
假设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)比较。 经过一些数学计算,这大致等于Kn-Kk = K.(nk)。
这是一个最好的情况,与独立搜索(Kn比较)相比,节省的费用并不那么显着。 无论如何,最糟糕的情况(所有搜索导致不平衡的分区)并不比独立搜索差。
更新 :这是最小比较合并问题的一个实例
在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.(nk)
。
任何这样的算法都不会被任何基于比较的方法打败,因此任何这样的算法都需要基本上与键的数量成比例的时间。
虽然不是最佳的,但实施起来要容易得多。
如果你有一组整数,并且你想搜索最小数量的比较,我想建议你从Knuth,6.2.1进行插值搜索。 如果二进制搜索需要Log(N)次迭代(和比较),则插值搜索仅需要Log(Log(N))操作。
有关细节和代码示例,请参阅:
我知道问题是关于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;
};
}
//基于递归二进制搜索的函数。 它返回给定数组中的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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.