确定与数组中的间隔匹配的值的最快方法

时间:2015-12-13 11:55:19

标签: c arrays performance

我有int的排序数组从xy(元素的值是随机的,但使用qsort()按升序排序)。该计划会收到<10;50><50;100>等各种时间间隔。我有以下简单的for循环来确定数组中的值是否在设置的时间间隔内,如果是,则向计数器添加一个。

 for(int i = 0; i < arraySize ;i++ )  {        
       if (points[i] >= interval1 && points[i] <= interval2){
            counter++;               
        }
    }

我需要比O(n)更快的方式来搜索数组,并确定points[i]中的值是否在设置的时间间隔内。价值可以达到数百万,因此会大幅放缓。

数组中的元素范围为0到1000000000(1e9)。间隔分别为。

4 个答案:

答案 0 :(得分:2)

使用二进制搜索 - 输入间隔[i, j],找到大于i的最小整数的索引,找到小于j的最大整数的索引,然后返回它们之间的距离。

ssize_t bin_search_first_larger(int arr[], size_t arr_sz, int val) {
    ssize_t l = -1;
    ssize_t r = arr_sz;
    /* invariant: arr[l] < val && val <= arr[r] */
    while (l+1 != r) {
        ssize_t m = l+(r-l)/2;
        if (arr[m] < val) {
            l = m;
        } else {
            r = m;
        }
    }
    /* l+1 == r && arr[l] < val && val <= arr[r] */
    return r;
}

ssize_t bin_search_last_smaller(int arr[], size_t arr_sz, int val) {
    ssize_t l = -1;
    ssize_t r = arr_sz;
    /* invariant: arr[l] <= val && val < arr[r] */
    while (l+1 != r) {
        ssize_t m = l+(r-l)/2;
        if (arr[m] <= val) {
            l = m;
        } else {
            r = m;
        }
    }
    /* l+1 == r && arr[l] <= val && val < arr[r] */
    return l;
}

ssize_t values_in(int arr[], size_t arr_sz, int x, int y) {
    ssize_t i = bin_search_first_larger(arr, arr_sz, x);
    ssize_t j = bin_search_last_smaller(arr, arr_sz, y);
    return j-i+1;
}

二进制搜索代码改编自Jon Bentley的 Programming Pearls (非常值得一读),其中显示了如何修改二进制搜索以返回第一次出现或最后一次出现具有重复项的排序数组中的值(而不是返回任意出现的重复值)。这个过程与您的用例类似,区别很小。

请注意,从概念上讲,假设arr[-1]为负无穷大,而arr[N]为正无穷大(其中N是数组的大小),但很明显,代码从不试图访问这些元素。

时间复杂度为O(log(N)),其中N是数组的大小,很难(不可能?)获得更好的数据。

我运行了一些测试,它似乎适用于一般情况和边缘情况(范围内没有元素,或者y大于每个元素,或者x小于每个元素,或者两个x小于每个元素,并且y大于每个元素),但是你可能知道这并不能证明没有错误。

答案 1 :(得分:1)

在聚会之后,尝试接受挑战以做得比O(log n)更好,这是O(1)(时间)解决方案,以获取给定范围内的值数{{1} }。

初始化本身,只做一次,是[a,b]O(MAXVALUE+NVALUES)是可能出现在数据集中的最高值,MAXVALUE是数据集中的值的数量。并根据问题

  
      
  • 0是最低值
  •   
  • 1,000,000,000是最高值
  •   
  • 数据集数百万
  •   

NVALUES要求程序分配O(1)数组。基本上对于1bn值,Linux x86_64上的MAXVALUE+1 int gcc )数组通常需要4 GB的RAM,或部分交换。这意味着程序必须在至少64位机器上运行。

要订购初始数据集。

原则

  • 初始化(一次):索引 i 1GB x sizeof(int)数组得到的值大于或等于 i

    < / LI>
  • m[0, 1bn]范围内的值只是[a, b]
    (如果m[a] - m[b+1]&gt; MAXVALUE,请改用b+1

初​​始化:

0

获取范围#define MAXVALUE 1000000000 #define NVALUES 1000000 int *m; // big array void initialization(int *values) { m = malloc((MAXVALUE+1) * sizeof(*m)); // check if NULL! int i,j; for(j=0,i=0 ; i<=MAXVALUE ; ) { if (j >= NVALUES) m[i++] = 0; else if (values[j] >= i) m[i++] = NVALUES-j; else j++; } } 中的值数:

[a, b] a<=b
在计算完所有范围后,必须释放

int count_in_range(int a, int b) { int ma = m[a]; int mb = b >= MAXVALUE ? 0 : m[b+1]; return ma-mb; }

答案 2 :(得分:0)

所需距离等于:

// position of first element greater than interval2
auto lb = std::upper_bound(array.begin(), array.end(), interval2);
// position of first element greater or equal than interval1
auto ub = std::lower_bound(array.begin(), array.end(), interval1);
// their difference is the number of elements in the needed range
return (ub - lb);

由于排序数组的下限/上限为O(log N),因此产生的复杂性为O(log N)

编辑:抱歉,没有注意到C标记。在C中,您需要自己实现下限/上限操作。为了让您的生活更加简单 - 您只能强制lower_bound,然后将upper_bound用作lower_bound(interval2 + 1)

答案 3 :(得分:-1)

这里你有其他版本的BinSearch,复杂度为O(logN)。

    int BinSearch(int *array, int first, int last, int value){

          int m;
          /* Optional Error control */
          if (!array || first<0 || last<first)  return -1;

          while (first <= last){

                  m = (first + last)/2;

                  if(array[m] == value) return m;

                  if(value < array[m]) last = m-1;

                  else
                       first = m+1;
            }
           /* Failure search */
           return -1;
     }

如果值不在数组中,或者值为索引的索引,则函数返回-1。

如果找到值或0,您可以执行返回1的变体,然后可以执行

      counter += BinSearch_variant(array,first,last,value);