用以下方法对数组中的元素求和的有效方法是什么

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

本文关键字:方法 求和 元素 和的 有效 是什么 数组      更新时间:2023-10-16

假设您得到一个n大小的数组A和一个整数k
现在你必须遵循这个功能:

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

求和最有效的方法是什么?

编辑:如果给我m(<=100000)查询,并且每次都给我一个不同的k,这将非常耗时。

如果查询集随每个k而更改,那么您不能比在O(n)中做得更好。您唯一的优化选项是使用多个线程(每个线程对数组的某个区域求和),或者至少确保编译器正确地向量化您的循环(或者使用内部函数手动编写向量化版本)。

但若查询集是固定的,并且只更改了k,那个么您可以使用以下优化在O(logn)中执行操作。

预处理数组。对于所有k,此操作仅执行一次:

  1. 对元素排序
  2. 制作另一个包含部分和的相同长度的数组

例如:

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

现在,当给出新的k时,您需要执行以下步骤:

  1. sortedArray中对给定的k进行二进制搜索——返回最大元素<=的索引k
  2. 结果为partialSums[i] + (partialSums.length - i) * k

如果可以对数组A[i]进行排序并准备一次辅助数组,则可以做得更好。

想法是:

  • 计算有多少项小于k,只需通过公式计算当量和:count*k
  • 准备一个辅助数组,它会直接给你高于k的项目的总和

准备

步骤1:对数组进行排序

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

步骤2:准备一个辅助数组

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

查询

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;
}

查询的复杂度为:O(log(N)),其中N是数组A的长度。

制备的复杂度为:O(N*log(N))。我们可以使用基数排序来处理O(N),但我认为它对您的情况没有用处。

参考

  • std::sort()
  • std::partial_sum()
  • std::lower_bound()

你所做的似乎绝对不错。除非这真的是非常关键的时间(也就是说,客户抱怨你的应用程序太慢,你测量了它,而这个功能就是问题所在,在这种情况下,你可以尝试一些不可移植的矢量指令,例如)。

通常,你可以从更高的层面来看待事情,从而提高效率。例如,如果我写

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

那么这将需要非常长的时间(增加5万亿),而且可以更快地完成。如果一次更改数组A的一个元素,并且每次都重新计算sum,也是如此。

假设数组A中有x个元素不大于k,集合B包含那些大于k且属于A的元素。

那么函数sum(k)的结果等于

k * x + sum_b

,其中sum_b是属于b.的元素的总和

您可以先对数组A进行排序,然后计算数组pre_A,其中

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

然后,对于每个查询k,在A上使用二进制搜索来找到不大于k的最大元素u。假设u的索引是index_u,则sum(k)等于

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

每个查询的时间复杂度是log(n)。

如果数组A可能被动态更改,您可以使用BST来处理它。