使用优先级队列从值列表中获取n个已排序元素(分页)

时间:2015-12-16 14:04:07

标签: java algorithm sorting elasticsearch

我的弹性搜索设置包含一些索引。我的用例是使用分页支持对我的应用程序中的数据进行排序(弹性搜索会对排序指数进行排序,但出于某些其他原因,我想在我的结尾处做);我的初衷是使用优先级队列(heap)来进行排序操作。

场景1 - 获取按照某些文件排序的前100条记录: 在这种情况下,每个弹性搜索索引都会为我提供第一个排序的100条记录,并且我会将所有这些值推送到我的PriorityQueue(,最大大小为100页大小)。逐个查看元素应该给我第1个排序的100条记录。

场景2 - 获取第10页的100条记录,按某些字段排序: 在这种情况下,每个弹性搜索索引都会给我排序1000(页面大小* 100)的记录,我需要从所有索引中准备整个排序的第10页的记录' 1000结果。现在,我的问题是,我可以通过仍然保持PriorityQueue 100的大小来获得所需的第10页记录吗?一个天真的想法是,如果输入排序顺序是递增的,则按降序将elemenet插入优先级队列,但我认为这种思想在某个地方存在逻辑上的缺陷 - 可能不是,但不能精确指出。

请帮忙。

2 个答案:

答案 0 :(得分:1)

离开上下文,算法问题是:

  

给定元素流,按顺序找到k“最大”元素。

我引用“最大”,因为我怀疑在上下文中,你实际上想要k个最小元素。这只意味着扭转所有比较的方向。但是,您可能希望k最小元素的顺序相反 - 即以最小元素结尾 - 在这种情况下,您需要在最后反转输出。

如果您确实需要从流中找到一些页面i,那么您可以先找到pagesize*i个最大元素,然后从该子集中获取pagesize个最小元素

根据您的建议,可以使用最大大小为Q最小优先级队列k来完成此操作:

  1. 对于每个传入元素x
    • 如果size(Q) < k,请将x添加到Q
    • 如果x > Min(Q),则弹出Q的最小元素,然后将x添加到Q
  2. 排序Q。这通常通过连续弹出元素来完成,因为经典优先级队列Pop操作包括交换底层矢量的第一个和最后一个元素,然后减小矢量大小。如果继续直到底层向量的优先级队列部分为空,则向量将由最大到最小的元素组成(因为最小的元素是第一个弹出的元素,因此位于向量的末尾)。
  3. 通过首先将k元素累积到向量中,然后根据上面的步骤1连续处理剩余元素,然后从此向量(成为优先级队列)生成堆,可以使效率略高一些。 / p>

    以上假设传入数据是单个未排序的流。但是,实际问题涉及合并几个已经排序的流。如果你有足够的内存来存储所有传入的数据,那么从合并结果中提取窗口有一个更好的算法,这是众所周知的找到k th 的算法的推广。两个排序列表合并的元素。请参阅this answer

    如果您不想解决上述问题,您也可以使用输入流的优先级队列。要弹出此优先级队列,请从优先级队列顶部的输入流中删除第一个元素,然后根据其新的第一个元素对该输入流进行删除。 downheap操作是O(m),其中m是流的数量(以及优先级队列的大小;可能是一小部分),你可以找到第一个k的最后n元素首先丢弃n - k元素,然后保留下一个k元素。

    实际上,这些可能不是解决问题的最佳方案。

    首先,您希望对已经由服务器排序的元素进行本地排序,这意味着所需的排序顺序与服务器使用的排序顺序不同。在这种情况下,分页可能不正确,并且算法将呈现不一致(并且可能令人惊讶)的结果。

    其次,迫使数十万个数据元素通过网络传输,以便在本地选择其中的数百个,这是对网络资源的巨大浪费。你可能不关心效率,但你可能应该关心传输时间。

    假设您认为不同的流在分布和大小方面都是合理无偏的。 (这可能是分片数据库的情况,但单独搜索的可能性较小。)在这种情况下,您可以从每个流请求一个数据窗口开始,大致对应于您所在页面的预期位置。寻找。 (也就是说,如果有m个服务器,并且您正在从合并结果中查找数据元素n-kn,那么您可能希望它们位于元素{{1}附近每个服务器上的(n-k)/m。所以你可以从覆盖该范围的每个服务器请求一个窗口。你想要多大的窗口取决于你对结果的偏好程度。

    如果事实证明窗口没有重叠,那么您将需要从一个或多个服务器请求更多数据。但是,您可以更好地了解每台服务器上的分布情况,这样您就可以启发式地改进对要请求的数据的预测。

    最终,这将收敛到您的合并请求包含所需范围的点。此解决方案的效率实际上取决于用于选择初始范围请求的启发式算法的质量,但在最好的情况下,它可以比来自每个服务器的n/m元素的强力请求更高效。

答案 1 :(得分:0)

如果我理解你的问题,这听起来像我过去必须处理的事情。如果我没记错的话,我无法做我想做的事情。如果你真的不能对ElasticSearch结束排序并且必须在你的结尾排序,如果你想分页所以你可以去任意页面,那么除了[gulp]下载整个索引,我想不出别的什么,排序,并输出正确的页面。

我对100元素优先级队列的要求有点困惑。这几乎听起来像是家庭作业。我不知道有什么方法可以对它进行排序并采取像这样的第k页,除了可能创建一个你正在排序的维度的直方图并得到你的排序的上限和下限。即使这样也不会给你一个完美的解决方案。

如果有人想到一种在本地进行排序和分页的方法,不涉及下载整个索引,或者使用100元素优先级队列进行排序和分页,请告诉我。