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

我尝试了两种方法。

  1. 使用 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
    }
    
  2. 先对数组进行排序,然后使用堆排序得到前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 公共库中,但可能真的更快。

标签: java algorithm performance sorting collections


【解决方案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

      【讨论】:

        猜你喜欢
        • 2013-06-26
        • 1970-01-01
        • 2022-06-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-31
        • 1970-01-01
        相关资源
        最近更新 更多