这是一个面试问题。我有K台机器,每台机器连接到一台中央机器。每台K机器都有一个4字节数字的数组。您可以使用任何数据结构将这些数字加载到这些机器上的内存中,并且它们适合。 K机器上的数字并不是唯一的。找到所有K台机器中数字组合中的K个最大数字。我能做到的最快的是什么?
答案 0 :(得分:12)
(这是一个有趣的问题,因为它涉及并行性。由于我以前没有遇到过并行算法优化,它很有趣:你可以逃避一些非常复杂的高复杂步骤,因为你可以以后弥补它。无论如何,回答......)
> " 我能做到的最快的是什么?"
你能做的最好的是O(K)。下面我将说明一个简单的O(K log(K))算法和更复杂的O(K)算法。
第一步:
每台计算机都需要足够的时间来阅读每个元素。这意味着除非元素已经在内存中,否则时间上的两个边界之一是O(最大数组大小)。例如,如果你的最大数组大小变化为O(K log(K))或O(K ^ 2)或其他东西,那么任何数量的算法技巧都不会让你走得更快。因此,技术上实际的最佳运行时间为O(max(K, largestArraySize))
。
让我们说阵列的最大长度为N,即<= K.通过上述警告,我们允许绑定N<K
,因为每台计算机必须至少查看一次其每个元素(每台计算机进行O(N)预处理),每台计算机都可以选择最大的K元素(这被称为查找k次序统计,请参阅这些线性时间algorithms)。此外,我们可以免费提供(因为它也是O(N))。
界限和合理期望:
让我们首先考虑一些最坏情况,并估算所需的最少工作量。
是否可以达到O(N)的界限?我们来看看......
简单方法 - O(NlogN + K)= O(KlogK):
现在让我们想出一个简单的方法,实现O(NlogN + K)。
考虑如此排列的数据,其中每列是计算机,每行是数组中的数字:
computer: A B C D E F G
10 (o) (o)
9 o (o) (o)
8 o (o)
7 x x (x)
6 x x (x)
5 x ..........
4 x x ..
3 x x x . .
2 x x . .
1 x x .
0 x x .
您还可以将此设想为来自计算几何的sweep-line algorithm,或者是&#39; merge&#39;的有效变体。从mergesort步骤。带括号的元素代表了我们用来初始化我们潜在的候选解决方案的元素&#34; (在某些中央服务器中)。该算法将通过转储两个未选择的o
的{{1}}个答案来收集正确的(x)
响应。
算法:
(旁注:添加回调挂钩以退出优先级队列是O(1)操作。)
我们可以用图形方式看到这将执行最多2K *(findNextHighest_time + queueInsert_time)操作,并且当我们这样做时,元素将自然地落在优先级队列之外。 findNextHighest_time是O(1),因为我们对数组进行了排序,因此为了最小化2K * queueInsert_time,我们选择具有O(1)插入时间的优先级队列(例如基于Fibonacci堆的优先级队列)。这给了我们一个O(log(queue_size))提取时间(我们不能有O(1)插入和提取);但是,我们永远不需要使用提取操作!完成后,我们只将转储优先级队列作为无序集合,这需要O(queue_size)= O(K)时间。
因此,我们具有O(N log(N)+ K)总运行时间(并行排序,然后是O(K)* O(1)优先级队列插入)。在N = K的最坏情况下,这是O(K log(K))。更好的方法 - O(N + K)= O(K):
然而,我提出了一种更好的方法,达到了O(K)。它基于median-of-median selection algorithm,但是并行化。它是这样的:
如果我们确定所有计算机中某处至少有K(不是严格地说)较大的数字,我们就可以删除一组数字。
算法:
o
个最高元素,并将该集合拆分为元素&lt;和&gt;它。这需要并行O(N)时间。sqrt(N)
个最高元素(让我们称之为“超级主义&” #39;),并注意哪些计算机具有统计信息&lt;和&gt;超级主义。这需要O(K)时间。事实证明,减少的总数是O(N)(令人惊讶的不是K阶),除了最后一步可能是O(K)。因此,该算法是O(N + K)= O(K)总数。
分析和模拟下面的O(K)运行时间。统计数据允许我们将世界划分为四个无序集合,在此表示为一个分为四个子框的矩形:
K/sqrt(N)
(我在这里绘制了无序行和s列之间的关系,但它会使事情变得混乱;现在请快速查看附录。)
对于此分析,我们将考虑N减少。
在给定的步骤中,我们可以消除标记为 ------N-----
N^.5
________________
| | s | <- computer
| | #=K s REDIST. | <- computer
| | s | <- computer
| K/N^.5|-----S----------| <- computer
| | s | <- computer
K | s | <- computer
| | s ELIMIN. | <- computer
| | s | <- computer
| | s | <- computer
| |_____s__________| <- computer
LEGEND:
s=statistic, S=superstatistic
#=K -- set of K largest elements
的元素;这已经从上面的矩形表示中删除了区域,将问题大小从K * N减少到,其中搞笑简化为
现在,具有uneliminated元素的计算机将其数据(上面的ELIMIN
矩形)重新分发到具有已消除元素(REDIST
)的计算机。这是并行完成的,其中带宽瓶颈对应于ELIMIN
的短大小的长度(因为它们的数量超过等待其数据的REDIST
计算机)。因此,数据传输的时间与ELIMIN
矩形的长度一样长(另一种思考方式:REDIST
是区域,除以K/√N * (N-√N)
每次数据,导致O(K/√N
)时间。
因此,在大小N-√N
的每个步骤中,我们都可以将问题大小减少到N
,但代价是执行K(2√N-1)
工作。我们现在递归。将告诉我们的表现的复发关系是:
N + 3K + (N-√N)
子问题大小的抽取比正常的几何系列快得多(√N而不是像你通常从普通的分而治之的N / 2那样)。不幸的是,主定理和强大的Akra-Bazzi定理都不起作用,但我们至少可以通过模拟来说服自己它是线性的:
T(N) = 2N+3K-√N + T(2√N-1)
函数>>> def T(n,k=None):
... return 1 if n<10 else sqrt(n)*(2*sqrt(n)-1)+3*k+T(2*sqrt(n)-1, k=k)
>>> f = (lambda x: x)
>>> (lambda n: T((10**5)*n,k=(10**5)*n)/f((10**5)*n) - T(n,k=n)/f(n))(10**30)
-3.552713678800501e-15
在大比例下是线性函数T(N)
的倍数,因此是线性的(输入加倍使输出加倍)。因此,这种方法几乎可以肯定地实现了我们推测的x
的界限。虽然看到附录中有一个有趣的可能性。
...
附录
O(N)
答案 1 :(得分:11)
更新:为了清楚起见,合并步骤不是一种排序。您只需从结果中选择前k个数字。有效地有很多方法可以做到这一点。例如,您可以使用堆,推动每个列表的头部。然后你可以从堆中移除头部并从元素所属的列表中推出头部。这样做k次可以得到结果。所有这些都是O(k * log(k))。
答案 2 :(得分:3)
答案 3 :(得分:1)
我会建议这样的事情:
按照排序顺序O(Nk)取每台机器上的k个最大数字,其中N是每台机器上的元素数
按最大元素对这些k元素的每个数组进行排序(你将得到k个元素的k个数组,按最大元素排序:方形矩阵kxk)
取k个元素的k个数组构成的矩阵的“上三角”,(k最大的元素将在这个上三角形中)
中央机器现在可以找到这些k(k + 1)/ 2个元素的k个最大元素
答案 4 :(得分:1)
答案 5 :(得分:0)
1)对每台机器上的物品进行分类 2)在中央机器上使用k - 二进制堆 a)使用每台机器的第一个(最大)元素填充堆 b)提取第一个元素,并将从您提取元素的机器中的第一个元素放回堆中。 (当然,在添加元素之后堆积你的堆)。
排序将为O(N log(N)),其中N是计算机上的最大数组。 O(k) - 构建堆 O(k log(k))提取并填充堆k次。
复杂度为max(O(k log(k)),O(N log(N)))
答案 6 :(得分:-1)
我认为MapReduce范例非常适合这样的任务。
每台机器运行它自己的独立map任务,以找到其数组中的最大值(取决于所使用的语言),这可能是每台机器上N个数字的O(N)复杂度。
reduce任务会比较各台机器输出的结果,为您提供最大的k数。