一维世界中到极点的平均距离
Posted
技术标签:
【中文标题】一维世界中到极点的平均距离【英文标题】:Average distance to a pole in a 1D world 【发布时间】:2017-11-19 01:51:45 【问题描述】:我的世界
想象一个有极点的一维离散世界。让我们用一维网格来表示这个世界,其中每个槽要么包含一个极点I
,要么不包含一个极点-
--------I-I---------I----II-------I-------------I--
在这个世界上有n
槽和m
杆。我们可以用长度为m
的向量来表示这个世界,列出两极的位置
std::vector<unsigned int> polePositions;
或者使用长度为n
的布尔向量
std::vector<bool> isThereAPole;
感兴趣的统计数据和示例
每个插槽到所有极点的平均距离 (averageDistance
)。比如槽位索引2以下(从零开始计数)
---I-I
到极点的平均距离
平均距离 = (1 + 3) / 2 = 2
然后,我们可以计算每个插槽的平均距离,并将它们平均得到平均平均距离 (averageAverageDistance
)。对于上面的例子,
平均平均距离 = ((3 + 5) / 2 + (2 + 4)/2 + (1+3)/2 + (0+2)/2 + (1 + 1)/2 + (2+0)/2)/6 = 12/6 = 2
问题
如何高性能计算这个averageAverageDistance
?
通常,在每次调用函数时,我将有大约 n=1e6 个插槽和大约 m=1e5 个极点。 n
将在每次调用函数时保持不变,但 m
(和 polePositions
或 isThereAPole
)会因函数调用而异。
实施不当
这里以上面的小数据为例进行简单的实现
#include <iostream>
#include <vector>
#include <math.h>
double getAverageAverageDistance(std::vector<unsigned int> polePositions, int n)
double averageAverageDistance = 0.0;
for (int slot = 0 ; slot < n ; slot++)
double averageDistance = 0.0;
for (auto& polePosition : polePositions)
averageDistance += fabs(slot - polePosition);
averageDistance /= polePositions.size();
averageAverageDistance += averageDistance;
averageAverageDistance /= n;
return averageAverageDistance;
int main()
std::vector<unsigned int> polePositions;
polePositions.push_back(3);
polePositions.push_back(5);
int n = 6;
std::cout << "averageAverageDistance = " << getAverageAverageDistance(polePositions, n) << "\n";
正确输出
averageAverageDistance = 2
这个程序的时间复杂度为 O(n m)。有没有更好的解决方案?
【问题讨论】:
对于两个相邻极点之间的每个线段,您可以轻松计算该线段上到极点的平均距离以及该线段上的元素数量。 如果我计算到最近极点的平均距离会很容易,但如果我计算到所有极点的平均平均距离,我认为它不起作用。我可能会误解你的建议。我是吗? 2 个问题:1) 你是怎么得到 11 的? 2) 如何 11/6 == 2.2? 为什么slot
是双精度的?与averageDistance
相同。你的位置是整数,向量索引是整数。
@KillzoneKid 谢谢。小错误已更正。
【参考方案1】:
下面是从头开始研究 6 个插槽的问题。
假设所有的插槽都已填满。然后,从每个插槽到 每隔一个插槽可以在 6 x 6 矩阵中表示为:
| 0 1 2 3 4 5 |
| 1 0 1 2 3 4 |
| 2 1 0 1 2 3 |
| 3 2 1 0 1 2 |
| 4 3 2 1 0 1 |
| 5 4 3 2 1 0 |
总距离可以通过将所有数字相加并除以来计算 总共 36 个。
当一个槽没有用一根柱子填满时,整个柱子都可以被移除。 比如说,第二个插槽不见了。您可以删除整个第二列 得到总和。当然,总和现在需要除以 30 和 不是 36 岁。
让我们可以表示一列中所有数字的总和。称它为
SUM(i)
其中i
是列的索引。
当第二行缺失时,总和可以表示为:
SUM(0) + SUM(2) + ... + SUM(5)
幸运的是,总和有一个很好的模式,您可以表示
SUM(i)
作为插槽总数和 i
的函数。
让我们看看N = 6
的列总和。
SUM(0) = 5*6/2
我们称之为基数,CSUM
。
SUM(1)
是从CSUM
中去掉 5 再加 1 得到的。
SUM(1) = CSUM - (5-1)
SUM(2)
是通过将CSUM
中的 5 和 4 去掉,然后加上 2 和 1 得到的。
SUM(2) = CSUM - (5-2) - (4-1)
=> SUM(2) = CSUM - (5-2) - (5-2)
=> SUM(2) = CSUM - 2*(5-2)
SUM(3)
是从CSUM
中去掉5、4、3,再加上3、2、1得到的。
SUM(3) = CSUM - (5-3) - (4-2) - (3-1)
=> SUM(3) = CSUM - (5-3) - (5-3) - (5-3)
=> SUM(3) = CSUM - 3*(5-3)
模式是这样的:
SUM(i) = CSUM - i*((N-1) - i)
一般情况下,
CSUM = (N-1)*N/2
有了这些知识,如果您知道
有极点的槽的索引。如果有,这是一个O(M)
操作
是M
两极。
演示程序:
#include <iostream>
#include <vector>
int SUM(int N, int p)
return (N-1)*N/2 - p*((N-1) - p);
int main()
int N = 0;
int M = 0;
std::cin >> N;
std::cin >> M;
std::vector<int> polePositions;
for ( int i = 0; i < M; ++i )
int p;
std::cin >> p;
polePositions.push_back(p);
int s = 0;
for ( int p : polePositions )
s += SUM(N, p);
double average = 1.0*s/(N*polePositions.size());
std::cout << "Average: " << average << std::endl;
给定输入
6
2
3 5
输出是
Average: 2
【讨论】:
【参考方案2】:我认为这可以在 O(m) 内完成。
polePositions
向量有每个极点的位置,也就是第一个槽到每个极点的距离。取这个向量的和,得到从第一个槽到所有极点的总距离(我们稍后会计算平均值)。
当您通过每个插槽时,总距离将减少m
,直到您到达位于位置p1
的带有杆的插槽。当我们到达那里时,您已经添加了(sum - m) + (sum - 2 * m) + ... + (sum - p1 * m)
。我们可以轻松跳过这个距离,并通过添加 (p1 * ((sum - m) + (sum - p1 * m)) / 2) 将其累加到总和中。
一旦我们通过第一个极点,向右的每一步都会将要加的项增加 1(随着我们离 p1 越来越远),同时随着我们越来越接近所有其他极点,将它减少 m-1。因此,您将重复上一步,为每个插槽添加 (sum - (m - 2))。
继续,直到您添加了每个极点的术语。最终你会到达中间,这个词会增加而不是减少。
对于最后一项,将最后一个极点右侧所有槽的总和相加。然后将总和除以n
。
(这是未调试的算法。)
【讨论】:
以上是关于一维世界中到极点的平均距离的主要内容,如果未能解决你的问题,请参考以下文章
清华世界第一!2020 USNews世界大学计算机科学排行榜;区块链平均薪资1.6万;Spring Framework5.2.1