【发布时间】:2015-09-09 02:32:19
【问题描述】:
我在一条直线上得到了 n 个点及其位置。我需要对每对点之间的距离求和。是否可以处理复杂度 O(n)。
示例:给定三个点,其坐标为 a(-1)、b(-3)、c(3)。 所需金额: |-1 + 3| + | - 1 - 3| + |-3 - 3 | = 12
请帮帮我。
【问题讨论】:
标签: algorithm dynamic-programming
我在一条直线上得到了 n 个点及其位置。我需要对每对点之间的距离求和。是否可以处理复杂度 O(n)。
示例:给定三个点,其坐标为 a(-1)、b(-3)、c(3)。 所需金额: |-1 + 3| + | - 1 - 3| + |-3 - 3 | = 12
请帮帮我。
【问题讨论】:
标签: algorithm dynamic-programming
计算每个连续片段的长度:
for (int i=0;i<n-1;i++) len[i]=x[i+1]-x[i];
请注意,这是针对已排序的点。如果不是,则在计算连续段长度之前进行排序。
计算每个分段在不同的成对距离中出现的次数:对于某些分段,成对数是 leftSidePoints*rightSidePoints。换句话说,您计算总和中每个段长度的贡献。
for (int i=0;i<n-1;i++) contributionOfSegment[i]=len[i]*(i+1)*(n-i-1);
i+1 是左侧点,n-i-1 是第 i 段的右侧点
答案是所有段的贡献之和:
int sum=0; for (i=0;i<n-1;i++) sum+=contributionOfSegment[i];
更新
几乎O(N) 算法,也不是O(Nlog(N))(标准排序),也不是O(maxX)(计算排序)。复杂度是O(N)loglog(maxX)),或者说更简单的O(N)*number_of_bits_in_maxX 是5N,用于几乎线性的32 位整数。
主要逻辑仍然如我上面所述。瓶颈点是排序 - 而O(N)*number_of_bits_in_maxX 因素是排序步骤。我们将使用Van Emde Boas tree 对数组进行排序。该树支持 findNext(x) 操作 - 在 x 之后以复杂度 O(loglogmaxX) 查找下一个元素。插入也有复杂性O(loglogmaxX)。
所以,Van Emde Boas 排序看起来像:
for(i=0;i<n;i++) tree.insert(x[i]) 在O(N)*number_of_bits_in_maxX 中填充树,其中x 是未排序的输入数组。O(N) 中查找最小值for(int i=1;i<n;i++) sortedArray[i] = tree.findNext(sortedArray[i-1])然后,使用我上面的逻辑,只需将数组:x 替换为 sortedArray
请注意,VEBTree 排序仅在理论上有意义,实际上它可能具有隐藏的常数因子,对于较小的 N,log(N) 可能比 loglog(maxX) 更好,因此,标准排序可能比树排序更快。如果 N 非常大而 maxX 只是 32 或 64 位整数,VEBTree 会很酷。
【讨论】:
log(N) 因素如此重要,提问者可能会在 O(N+maxN) 中应用计算排序(如果数字小到可以作为计算排序数组中的索引)。看起来在这种情况下,它在复杂性和逻辑上都会几乎类似于 displayName 的想法。
如果点按排序顺序,这是可行的。让我们举一个简单的例子,大于 n=3,因为n*(n-1)/2(所有可能的没有重复的对,其中 (a,b) 和 (b,a) 被认为是重复的)在这种情况下也是 3 并且具有误导性:
n = 4 // number of points
p = [-3, -1, 1, 3] // our points
我们将首先计算到第一个点p[0] 的所有距离,这是一个O(n) 操作,结果是12,因为|-3 + 1| + |-3 - 1| + |-3 - 3| = 2 + 4 + 6 = 12。
我们现在观察到,由于这些点在一条线上,并且下一个点将在第一个点的右侧,所以我们可以通过简单地从上一个点减去当前点和上一个点之间的距离来计算所有距离相应的点总和:
12 - (k - 1) * dist = 12 - (4 - 1) * 2 = 12 - 6 = 6
由于点 0 和 1 之间的距离等于 2,我们需要为每个先前计算的距离减去这个值(前一点是 k-1=3 对的一部分)。在下一次迭代中,k 将小 1:
6 - (k - 1) * dist = 6 - (3 - 1) * 2 = 6 - 4 = 2
所以最终初始总和将是O(n),然后我们将不得不做O(1) n 次得到O(n)。
这将为我们生成一个部分和数组[12,6,2,0]=20,您无需存储它,只需将其可视化即可。
【讨论】:
如果对点进行排序,我可以想办法。假设我们有n 点。我们考虑两个相邻的点Pi和Pi+1,假设Pi和其他Point之间的距离是Di,Pi和Pi+1之间的距离是d,那么Di+1 = Di + i * d - (n - i - 1) * d,那么如果从左相邻点到所有其他点的距离已知,我们可以计算 O(1) 中一个点到所有其他点的距离。我们只需要计算第一个点,并进行相应的更新。
等式的逻辑是,当从Pi移动到Pi+1时,从Pi+1到它左边所有点的距离都增加了d,从Pi+1到它右边的所有点都减少了d。
【讨论】:
无论点是排序还是未排序,都存在线性排序时间的可能解决方案。所以,让我们从点n+1 未排序的点开始。如给定的那样,这些点沿着一条线(假设是 x 轴)并且它们只有整数 x 值。假设第一个点是 P0,最后一个点是 Pn。这意味着总点数为n+1,总点距离为n。
minX) 和最大值 (maxX) x 值;BitArray[max - min + 1]);BitArray[minX + currentX] = true;int Distances[n] 数组并开始遍历BitArray 中的所有值。
minX;Distances[0] = thisX - minX;Distances 数组中。到目前为止,运行时间复杂度为 O(maxX - minX),它是线性的。对于足够接近的点,这将是 O(n)。此外,我们还创建了一个数组,它告诉我们 (P0, P1) 与索引 0 之间的距离,(P1, P2) 与索引 1 之间的距离,(P2, P3) 与索引 2 之间的距离等等。
点沿x轴的排列会是这样(虚线是x轴,每个*是一个点,dn是P(n-1)和Pn之间的距离),
---*----------*------*--------*---------....--------*---
^ ^ ^ ^ ^
P0 (d0) P1 (d1) P2 (d2) P3 (d3) .... d(n) Pn
现在,计算是 O(n)。只是一个简单的总结。
总和是:
(1 * (n - 1) * Distances[0]) +
(2 * (n - 2) * Distances[1]) +
(3 * (n - 3) * Distances[2]) +
.
.
.
(1 * (n - 1) * Distances[n-1])
我是如何得出这个总结的
以 P0 为例。 P0到P1到Pn的距离之和
= d(P0, P1) + d(P0, P2) + ... + d(P0, Pn)
= d[0] + (d[0] + d[1]) + ... + (d[0] + d[1] till d[n-1])
= (n-1)*d[0] + (n-2)*d[1] + ... + (n-1)*d[n-1]
我们以同样的方式取 P1 并计算它从 P2 到 Pn 的距离
然后取 P3... 直到最后只考虑 P(n-1) 和 Pn 之间的距离
将这些距离相加,我们直接得到我上面提到的公式。
因此,如果点已排序,运行时间为 O(n),如果点未排序,正在运行时间是 O(maxX - minX),仍然线性增长。
【讨论】: