如何从 Java 中的未排序数组中快速获取前 N 个出现项?

Posted

技术标签:

【中文标题】如何从 Java 中的未排序数组中快速获取前 N 个出现项?【英文标题】:How to fast get top N occurence items from an unsorted array in Java? 【发布时间】:2013-06-17 03:52:05 【问题描述】:

我尝试了两种方法。

    使用 HashMap 计算每个项目的计数,然后导航地图

    HashMap<Integer, Integer> doc_counts = new HashMap<Integer, Integer>();
    for (int i = 0; i < p; ++i) 
        int doc = alld[i];
        Integer count = doc_counts.get(doc);
        if (null == count)
            count = 0;
        doc_counts.put(doc, count + 1);
    
    // to now it cost 200ms already
    for (Entry<Integer, Integer> item : doc_counts.entrySet()) 
        heapCheck(h, hsize, item.getKey(), item.getValue());    // heap sort top hsize items
    
    

    先对数组进行排序,然后使用堆排序得到前N个。

    Arrays.sort(alld, 0, p); // the sort costs about 160ms
    int curr = alld[0];
    int count = 0;
    for(int i = 0; i < p; i++) 
        int doc = alld[i];
        if(doc == curr) 
            ++count;
         else 
            ++nHits;
            //curr += base;
            heapCheck(h, hsize, curr, count);
            curr = doc;
            count = 1;
        
    
    //
    // Handle the last document that was collected.
    heapCheck(h, hsize, curr, count);
    

对一个包含 1,600,000 个元素的数组进行测试表明,第二种方法花费了大约 170 毫秒,并且大部分时间都花在了排序上(大约 160 毫秒),而第一种方法花费了 200 毫秒,即使只是将所有元素添加到 HashMap 中。如何提高性能、找到更快的映射或排序函数或将其更改为并行函数以使用多线程?

【问题讨论】:

@assylias 您提到的 3 个链接是关于另一个问题“前 N 个”,但不是“前 N 个出现”。事实上,heapCheck 是“top N”问题的最佳解决方案,但它只是整个问题的一部分。 抱歉,我看错了你的问题。 你试过第一个HashMap&lt;Integer, Integer&gt; doc_counts = new HashMap&lt;Integer, Integer&gt;(alld.length, 1.0f);吗? @assylias 这个我试过了,性能提升不大,还是170ms左右 您是否尝试过使用 IntMap 或 multisets ?它们不在 Java 公共库中,但可能真的更快。 【参考方案1】:

堆排序是 O(n log n),而将所有内容添加到 Hashmap 是 O(n),因此很可能由于 Hashmap 的大小调整/重新散列,您会遭受恒定因素的性能影响。尝试指定较大的初始容量以避免过多的调整大小操作。

【讨论】:

我试过了,性能提升不大,还是170ms左右。 如果不是不断替换映射中的整数,而是创建一个可变的class Count public int value;,然后,找到正确的实例,增加它包含的计数。这将使地图查找次数减半。【参考方案2】:

该任务非常适合并行化。您可以使用FokJoinPool framework 来实现分而治之的算法。例如,您可以使用并行排序算法对数组进行排序并减少 160 毫秒。

或者,如果您想试验 Java 8,它有一个内置的 Arrays.parallelSort() 方法。

【讨论】:

【参考方案3】:

带有原始类型的Collection 框架非常昂贵。

尝试使用GNU Trove TIntIntHashMap 代替第一种方法,即计数图。

根据我的观点和经验,第二个应该更快,特别是如果您已经在内存中拥有数据,并且可以使用原始排序,这比排序对象快得多。

【讨论】:

【参考方案4】:

不要排序 - 这是 O(n log n)。有一个 O(n) + O(N log N) 的解决方案:

创建一个Map&lt;Integer, Integer&gt; 来保存每个数字 O(n) 的计数 通过数组创建/更新计数 O(n) 在地图上通过一次,保持前 N 最大,可能使用导航地图 O(N log N)

如果 N

【讨论】:

以上是关于如何从 Java 中的未排序数组中快速获取前 N 个出现项?的主要内容,如果未能解决你的问题,请参考以下文章

重复大量输入的快速选择算法?

Java排序算法之快速排序

快速排序(java)

快速学习插入排序

如何从T-SQL中的排序表中从第M行开始获取N行

java排序之插入排序(直接插入排序和希尔排序)