简体   繁体   中英

Whats the efficient way to sum up the elements of an array in following way?

Suppose you are given an n sized array A and a integer k
Now you have to follow this function:

long long sum(int k)
{
    long long sum=0;
    for(int i=0;i<n;i++){
        sum+=min(A[i],k);
    }
    return sum;
}

what is the most efficient way to find sum?

EDIT: if I am given m (<=100000) queries, and given a different k every time, it becomes very time consuming.

You can do way better than that if you can sort the array A[i] and have a secondary array prepared once.

The idea is:

  • Count how many items are less than k , and just compute the equivalent sum by the formula: count*k
  • Prepare an helper array which will give you the sum of the items superior to k directly

Preparation

Step 1: sort the array

std::sort(begin(A), end(A));

Step 2: prepare an helper array

std::vector<long long> p_sums(A.size());
std::partial_sum(rbegin(A), rend(A), begin(p_sums));

Query

long long query(int k) {
  // first skip all items whose value is below k strictly
  auto it = std::lower_bound(begin(A), end(A), k);

  // compute the distance (number of items skipped)
  auto index = std::distance(begin(A), it);

  // do the sum
  long long result = index*k + p_sums[index];
  return result;
}

The complexity of the query is: O(log(N)) where N is the length of the array A .

The complexity of the preparation is: O(N*log(N)) . We could go down to O(N) with a radix sort but I don't think it is useful in your case.

References

If set of queries changes with each k then you can't do better than in O(n). Your only options for optimizing is to use multiple threads (each thread sums some region of array) or at least ensure that your loop is properly vectorized by compiler (or write vectorized version manually using intrinsics).

But if set of queries is fixed and only k is changed, then you may do in O(log n) by using following optimization.

Preprocess array. This is done only once for all k s:

  1. Sort elements
  2. Make another array of the same length which contains partial sums

For example:

inputArray: 5 1 3 8 7
sortedArray: 1 3 5 7 8
partialSums: 1 4 9 16 24

Now, when new k is given, you need to perform following steps:

  1. Make binary search for given k in sortedArray -- returns index of maximal element <= k
  2. Result is partialSums[i] + (partialSums.length - i) * k

What you do seems absolutely fine. Unless this is really absolutely time critical (that is customers complain that your app is too slow and you measured it, and this function is the problem, in which case you can try some non-portable vector instructions, for example).

Often you can do things more efficiently by looking at them from a higher level. For example, if I write

for (n = 0; n < 1000000; ++n)
   printf ("%lld\n", sum (100));

then this will take an awful long time (half a trillion additions) and can be done a lot quicker. Same if you change one element of the array A at a time and recalculate sum each time.

Suppose there are x elements of array A which are no larger than k and set B contains those elements which are larger than k and belongs to A.

Then the result of function sum(k) equals

k * x + sum_b

,where sum_b is the sum of elements belonging to B.

You can firstly sort the the array A, and calculate the array pre_A, where

pre_A[i] = pre_A[i - 1] + A[i] (i > 0),
        or 0 (i = 0);

Then for each query k, use binary search on A to find the largest element u which is no larger than k. Assume the index of u is index_u, then sum(k) equals

 k * index_u + pre_A[n] - pre_A[index_u]

. The time complex for each query is log(n).

In case array A may be dynamically changed, you can use BST to handle it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM