【问题标题】:Optimal algorithm for returning top k values from an array of length N从长度为 N 的数组返回前 k 个值的最佳算法
【发布时间】:2011-06-24 19:17:12
【问题描述】:

我有一个包含 n 个浮点数的数组,我希望返回前 k 个 (在我的例子中 n ~ 100, k ~ 10)

这个问题有已知的最优解路径吗?

谁能提供一个C算法?

编辑:实际上这里有两个问题:已排序和未排序。我对 unsorted 感兴趣,应该更快!

【问题讨论】:

标签: algorithm sorting


【解决方案1】:

以下是 Java 中基于堆的优雅解决方案,复杂度为 O(nlogK)。这不是最有效的,但我认为它很容易理解。如果您想要基于浮点的解决方案,可以将 Integer 更改为 Float

import java.util.Arrays;
import java.util.PriorityQueue;

public class FindKLargest {

public static void find(int[] A, int k) {

    PriorityQueue<Integer> pq = new PriorityQueue<>(k);// Min heap because the element has to be greater
                                                        // than the smallest element in the heap in order
                                                        // to be qualified to be a member of top k elements.
    for (int i = 0; i < A.length; i++) {
        if (i < k) // add until heap is filled with k elements.
            pq.add(A[i]);
        else if (pq.peek() < A[i]) { // check if it's bigger than the
                                        // smallest element in the heap.
            pq.poll();
            pq.add(A[i]);
        }
    }
    int[] topK = new int[pq.size()];
    int index = 0;
    while (index != k)
        topK[index++] = pq.poll();
    System.out.println(Arrays.toString(topK));
}

public static void main(String[] args) {
    int[] arr = { 1, -2, -3, -4, -5 };
    find(arr, 4);
}

}

【讨论】:

    【解决方案2】:

    如果你有一个花哨的 gpu,我可以告诉你如何同时计算巨大的 n 个实例的顶部 k,所以将它们分散到纹理上,每个实例,然后将它们的混合添加到纹理上“高度”作为沿纹理的位置。

    但请注意,您必须猜测一个可接受的范围或知道它,否则您不会传播到您可能拥有的最大细节。

    你克隆位置。 (你应该得到一个 2,如果上面有 2,如果上面有 10,你应该得到 10。)在所有实例中。 (只需在 8192x8192 纹理,这些“高度”框中的 64x64 上说所有内容。)您还可以跳过计数为 0 的插槽。

    然后做一个 mipped add 层次结构,除了你像二叉树一样做,你只把它当作它的 1 维,所以取前面的 2 个数字并将它们加在一起,并继续对每个二进制 mip 做。

    然后我们使用这些 mip(已收集计数)来发现 k 的大致位置,使用进程中的所有 mip,在最后一个线程上执行此操作,您将从中取出大块,然后慢慢使用更多详细的 mips 以找到 k 所在的每个像素值。

    这样做更有意义,如果它都被再次实例化,那么它是每个阈值发现的一个线程。 (只是说你一次运行了一个 ANN 128x128 次,(翻译不变性有人吗?)那么这很有意义。

    并达到该计数的阈值高度,但它是近似值......所以你得到一个近似值 k。对于 n 个列表。

    你可以做更多的工作来获得精确的 k,但是在相似匹配中,但如果你可以避免它是近似的,比如它获得了前 ~k 个激活,那么就不用担心了。

    【讨论】:

    • 但是麦格!为什么拥有近似的 top k 很重要?我认为这对于更便宜的相似性匹配很重要!!!
    【解决方案3】:

    方法一

    由于 k 很小,你可以使用锦标赛方法找到第 k 个最大的。这种方法在 Knuth 的编程艺术,第 3 卷,第 212 页中有所描述。

    首先在 n-k+2 个元素上创建一个锦标赛。有点像网球淘汰赛。首先,你分成两对并比较这对的成员(就好像这两个人打了一场比赛,一个输了)。然后是获胜者,你再次分成对等,直到你有一个获胜者。您可以将其视为一棵树,获胜者位于顶部。

    这需要 n-k+1 次精确比较。

    现在这些 n-k+2 的获胜者不可能是你的第 k 个最大元素。考虑一下它在锦标赛中的路径 P。

    现在从剩下的 k-2 中选择一个,然后沿着路径 P 前进,这将为您提供一个新的最大路径。基本上,您可以用 k-2 元素之一替换之前的获胜者来重做锦标赛。设 P 为新获胜者的路径。现在从 k-3 中选择另一个,然后沿着新路径向上,依此类推。

    最后,在你用完 k-2 后,用 -infinity 替换最大的,锦标赛中最大的将是第 k 个最大的。您丢弃的元素是前 k-1 个元素。

    这最多需要n - k + (k-1) [log (n-k+2)] 比较才能找到前 k 个。它虽然使用 O(n) 内存。

    就比较次数而言,这可能会超过任何选择算法。

    方法二

    作为替代方案,您可以维护一个包含 k 个元素的最小堆。

    首先插入 k 个元素。然后对于数组的每个元素,如果它小于堆的最小元素,则将其丢弃。否则,删除堆的最小值并从数组中插入元素。

    最后,堆将包含前 k 个元素。这将需要 O(n log k) 比较。

    当然,如果 n 很小,只需对数组进行排序就足够了。代码也会更简单。

    【讨论】:

    【解决方案4】:

    简短回答:不。

    更长的答案:是的,已知几种互不相容的最优解。这取决于 n、k 以及您可以保证数组的哪些属性。

    如果您对数组一无所知,那么复杂度的下限显然是 O(n),因为必须检查源数组的所有元素是否适合前 10 个元素。如果您对源数组一无所知这允许安全地跳过元素,您应该使用该知识。

    同样,复杂度上限是 O(n.log(n)),因为您总是可以选择通过对数组进行排序 (O(n.log(n)) 并返回前 10 个项目 (O( 1))。

    线性搜索将每个项目与迄今为止找到的第十个最高项目进行比较,并在需要时将其插入到迄今为止发现的最高项目列表中的适当位置,这对于平均和最佳情况场景具有相似的复杂性,并且最坏情况为 O(kn),明显优于 O(n-squared)。对于您估计的尺寸,我希望这种方法表现良好。

    如果 n 大得多(~10000)并且 k 以相同的比例增加,那么实现快速选择算法可能是值得的。您想要的元素越多,快速选择的性能越好。但是,如果 k 没有随着 n 的比例增加,你应该坚持线性搜索。 Quickselect 和朋友会修改原始数组,因此如果您无法就地执行此操作,则不太适合,因为您需要更多存储空间和大量算法复杂性不包括的复制。

    如果 n 很大(~1e20),您可能希望从输入数组的多个分区中的每个分区中找到最大的 k,然后从这些结果的聚合中找到最大的 k,这样您就不会尝试一次分析超出内存容量的数据,并允许高效地并行化操作。

    【讨论】:

      【解决方案5】:

      您可以使用selection algorithmO(n) 中执行此操作。用分区算法找到kth 最大的元素,那么它后面的所有元素都会比它大,那些就是你最上面的k

      如果您需要按排序顺序排列的顶级k,则可以将它们排序为O(k log k)

      【讨论】:

      • 请注意这个算法是O(2n),最终是O(n),但对于许多实际应用来说非常慢。
      • 2n 步仍然比 nlogn 或 nlogk 步快得多,除非你有一个非常小的 n 或 k。对于以 2 为底的对数,k 必须小于或等于 4,该算法的效率才会低于 nlogk 解决方案。
      猜你喜欢
      • 2010-09-12
      • 2019-07-13
      • 1970-01-01
      • 2015-12-13
      • 2012-01-30
      • 1970-01-01
      相关资源
      最近更新 更多