我有一个长度N=10^5
的数组对于每个索引1< = i< = n
我必须计算A[j]-A[i] and (j-i) is prime and j>i
这是我的代码:
for(int i=1;i<=n;i++){
for(int j=0;j<prime.size();j++){
int x = prime.get(j);
if(x+i>n) break;
ans+= A[x+i]-A[i];
}
}
我应该如何更快地完成这项工作?我认为时间复杂度是 O(N * prime.size)
答案 0 :(得分:1)
首先,我会重新解释你的问题,以便说出我认为你想要实现的目标。您可能正在查找A[j]-A[i]
形式的差异总和,其中(j-i)
是&#34;正面&#34;素数和1<=i<j<=N
。记住这句话......
我们计算A[k]
添加到总和(由p
表示)的次数以及从总和中减去A[k]
的次数(由{{1}表示})。好吧,m
等于区间m
中的素数数量,而[1,N-k]
等于区间p
中的素数数量。如果您不相信我,请逐步模拟您的代码所做的事情。然后你可以这样做:
[1,k-1]
现在,我们需要找到一种方法来有效地为S = 0
for k = 1 to N do
S = S + (p(k) - m(k)) * A[k]
endfor
区间中的每个p
确定m
和k
。我看到你已经构建了一个似乎是有序的素数列表。那么,要回答一个表格的查询&#39;区间[1,N]
中有多少素数?&#39;您可以在[1,t]
的列表上执行二进制搜索。这会使复杂性降至t
。
作为替代方案,您可以预先计算表单查询的答案&#39;区间O(N*log(prime.size))
中有多少素数?&#39;。你需要一个大小为[1,t]
的额外数组nrPrimesLessThan
来保存结果,做这样的事情来计算它的值:
N
预计算部分采用count = 0
for i = 1 to N do
if i < prime.get(count) then
nrPrimesLessThan[i] = count
else
count = count + 1
nrPrimesLessThan[i] = count
endif
endfor
个步骤,但现在一个查询需要O(N)
个步骤,因此计算总和需要O(1)
个步骤。总体而言,线性时间为O(N)
。
答案 1 :(得分:0)
从您的代码示例来看,您希望将数组中所有值对的差异求和,其中索引的差异为素数。你已经有了一系列素数。
下图显示了如何减去和添加元素:
0 1 2 3 4 5 6 7 8 9
- + + + + 0
- + + + + 1
- + + + + 2
- + + + 3
- + + + 4
- + + 5
- + + 6
- + 7
- 8
- 9
+
表示将元素添加到总和中。 -
表示从总和中减去元素。这不是一个减法;对于左侧的每次加法都会发生减法,因此将A[0]
减去4次。永远不会添加。
另一方面,A[9]
永远不会被减去,但会增加四次。通常,每个元素的减去次数与行中的加号相同,并且添加的次数与列中的加号相同。这里有一个对称性:
add[i] = sub[N - i - 1]
表示从零开始的指数。 add[i]
的价值是多少?它是小于或等于i
的素数的数量。
以下是add
数组被称为m
的代码示例:
int psum2(const int A[], int n)
{
int sum = 0;
int m[n];
int j = 0;
int k = 0;
for (int i = 0; i < n; i++) {
if (i == prime[j]) {
k++;
j++;
}
m[i] = k;
}
for (int i = 0; i < n; i++) {
sum += (m[i] - m[n - i - 1]) * A[i];
}
return sum;
}
如果您需要更频繁地执行求和,则数组m
始终相同并且可以预先计算。算法是O(n)。
问题也与素数的核心无关。上述方法适用于差异的所有条件和,其中索引的差异必须在一组数字中进行处理。