给定一个数组(仅包含正个整数)已经有第一个 k 元素: a 1 ,一个 2 ,.... a k 。
我需要填充剩余的(n - k)元素(数组总共有 n 个元素)。
n 的值约为 10 ^ 3 且 1 <= k <= n 。
每个 a i 的值是两个数字的最小和,使得这两个数字的位置之和等于 i 。
这是伪代码(我的算法):
for i = k + 1 to n
a[i] = max_value
for j = 1 to (i / 2)
a[i] = min(a[i], a[j] + a[i - j])
时间复杂度: O(n ^ 2)
问题:还有其他方法可以更快地完成这项工作吗?
我正在寻找能够在小于 O(n)中找到每个 a i 的值的任何数据结构或算法。
P / S:这是我程序中的一个程序,所以我需要尽快完成。
答案 0 :(得分:0)
您可以通过使用线程并行检查最小值来提高程序速度。例如,您可以运行4个线程,每个线程检查j的范围的1/4。这将略微提高速度,但您的算法仍将需要O(n ^ 2)运行时间。
我同意你最有可能超越O(n ^ 2)的评论。所以你最好的选择可能是尝试这样的事情来优化你的代码,以减少n ^ 2前面的系数。
答案 1 :(得分:0)
AFAICT这不会对O(n ^ 2)提供有保证的改进,但它应该在实践中将内循环周期的数量减少很多。基本思想是我们可以以不同的顺序在内循环中测试对,这使我们能够在很多时候提前完成。具体来说,我们首先制作一个排序的数字位置列表,并将其存储在s[]
中,以便a[s[i]]
是a[]
中的第i个最小数字。然后在主内循环中,我们使用a[s[j]]
而不是a[j]
(和a[i - s[j]]
而不是a[i - j]
)以第一项的递增顺序形成对和。这为我们提供了两种方法来提前停止内循环:
a[s[j]] >= a[i]
,那么我们可以停止,因为每个总和必须更大,因为每个总和的第一项(a[s[j+1]]
等)必须至少与最佳解决方案一样大远(已经在a[i]
),而另一个术语永远不会消极。a[i - s[j]] <= a[s[j]]
(即,如果&#34;伙伴&#34;下一个最小数字小于或等于它),我们可以停止,原因更复杂。相反,假设后来有一些更好的配对a[s[m]] + a[i - s[m]]
(即m > j
)。我们知道第一个词a[s[m]]
,必须至少与我们当前的第一个词a[s[j]]
一样大,因为我们按递增顺序访问第一个词,m > j
;因此,对于总和a[s[m]] + a[i - s[m]]
要比a[s[j]] + a[i - s[j]]
更好(即更少),它的第二个术语a[i - s[m]]
必须小于我们当前的第二个术语{{{ 1}}。 (这不是一个充分条件,但这并不重要。)但是,由于我们刚刚观察到a[i - s[j]]
,我们也知道a[i - s[j]] <= a[s[j]]
,这意味着a[i - s[m]] < a[s[j]]
必须在我们之前已经处理过的一对总和中出现作为第一个术语!这与a[i - s[m]]
相矛盾,这意味着没有更好的配对可以存在,所以我们可以安全地停止。我希望第二个条件可以消除很多内循环周期;第一个可能只对数据集有很大帮助,这些数据集中有一些小数字和很多非常高的数字,并且可以用小数字的总和来覆盖大多数位置。
奖金效率:如果我们实施上述第二个条件,那么我们实际上并不需要单独的m > j
循环终止测试,因为在检查了任何j < i / 2
对和之后,我们必须遇到至少有一对和两次(一次交换第一和第二项),这将导致条件2触发并退出循环。
伪代码:
i / 2 + 1