这是Internet上常见的插值搜索算法的C / C ++实现。但是,当与大约100000整数的排序数组一起使用时,中间变量开始生成负数组索引,从而导致分段错误。可能是什么问题?
#include <stdlib.h> #include <stdio.h> #include <time.h> int interpolationSearch(int sortedArray[], int toFind, int len) { // Returns index of toFind in sortedArray, or -1 if not found int low = 0; int high = len - 1; int mid; while (sortedArray[low] <= toFind && sortedArray[high] >= toFind) { mid = low + ((toFind - sortedArray[low]) * (high - low)) / (sortedArray[high] - sortedArray[low]); if (sortedArray[mid] < toFind) { low = mid + 1; } else if (sortedArray[mid] > toFind) { high = mid - 1; } else { return mid; } } if (sortedArray[low] == toFind) return low; else return -1; // Not found } int main(void) { srand(time(0)); int arr[100000]; for (int i=0; i<100000; i++) { arr[i] = rand()%100000; } int length = sizeof(arr)/sizeof(int); qsort(arr,length,sizeof(int),order); for (int j=0; j<10000; j++) { interpolationSearch(arr,rand()%100000,length); } }
子表达式: ((toFind - sortedArray[low]) * (high - low))
((toFind - sortedArray[low]) * (high - low))
…可以很容易地评估为: ((99999-0) * (99999-0)) == 99999^2
((99999-0) * (99999-0)) == 99999^2
…比2 ^ 31(== 32位有符号整数的范围)大得多。
一旦超过2 ^ 31-1,整数将溢出为负数,因此为负索引。如果它超过2 ^ 32(它也可以这样做),那么(很可能在技术上是未定义的)您将丢失高阶位,并且最终将得到有效的随机偏移,包括正负值。
为避免所有这些情况,您需要仔细进行数学运算,以确保所有子表达式都不会产生整数溢出。通常,最简单的方法是将其转换为浮点数,其范围比32位整数大许多数量级。
在最终分析中,像这样的用于二进制搜索的插值通常是不值得的-计算插值的开销通常大于它“保存”的循环的几次额外迭代。