【问题标题】:How to efficiently find 10 greatest numbers from billions of numbers?如何有效地从数十亿个数字中找到 10 个最大的数字?
【发布时间】:2017-01-15 07:38:05
【问题描述】:

问题说明:从包含数十亿数字的文件中找出最大 10 个数字

输入: 97911 98855 12345 78982 ..... .....

我实际上想出了以下解决方案

  • 最佳案例复杂度O(n) - 当文件中的数字按降序排列时
  • 最坏情况复杂度O(n*10) ~ O(n)当文件有数字升序时
  • 平均 复杂性~O(n)

空间复杂度在所有情况下都是O(1)

我正在使用文件阅读器和一个存储最多 10 个数字的排序数组来读取文件。我将检查 currentLine 是否大于数组中的最小元素 - 如果是,将通过交换将其插入正确的位置。

Scanner sc = new Scanner(new FileReader(new File("demo.txt")));
int[] maxNum = new int[10];
    while(sc.hasNext()){
    int phoneNumber = Integer.parseInt(sc.nextLine());
    if(phoneNumber>maxNum[9]){
        maxNum[9] = phoneNumber;
        for(int i =9;i>0;i--){
            if(maxNum[i]>maxNum[i-1]){
                int temp = maxNum[i];
                maxNum[i] = maxNum[i-1];
                maxNum[i-1] = temp;
            }
        }
    }
    }

如果有更好的方法来实现,我正在寻找反馈

【问题讨论】:

  • FYR,O(n*10)O(n) 相同。
  • 你可以使用内置的方法来找到最大值,当你找到一个最大值时存储这个值然后删除它,然后再做10次。
  • @Null 。 .你建议哪种内置方法..它不需要多次传递和更多迭代
  • 检查this,它会帮助你。
  • 值有上限吗?

标签: java arrays algorithm


【解决方案1】:

如果文件未排序,您必须对文件中的每个数字至少查看一次,因为它可能位于最大的 10 个中。因此 O(n) 是你能达到的最好的。

通过将maxNum 数组替换为最小堆,可以进行一些优化(但不会改变渐近复杂度)。如果要找到的数字数量足够大(假设您正在寻找 100 个最大的数字),这将运行得更快。它可能不会在 10 点还清。

【讨论】:

  • 是的,如果需要的 maxNumbers 更多,那就是真的.. 但是对于 10 个数字,如您所说,数组会快得多
  • 我看不出有什么理由不在这里使用min heap,这个实现有方式更多的交换操作成本,没有必要让它们保持排序,从堆顶轮询时只需执行此操作一次
  • @Xlee 。 。当然 。 .将运行一些基线测试并查看差异
  • 这个答案只是说 OP 正在做的事情很好,但事实并非如此。在最好的情况下,它可以是评论。事实上这个答案应该被删除。
  • @SaeedAmiri 显然,我有不同的看法。对于单线程解决方案,OP 接近最佳。按照您的建议使用并行处理归结为“购买更强大的机器”,因此速度更快。
【解决方案2】:

您可以通过多线程和并行化改进算法。这意味着运行,例如20个线程,将文件分成20个文件,在每个部分中找到最大的10个数字。最后,在您维护的 20 个数组(每个长度为 10)中找到最大的 10 个数字。

关键是操作是从文件或数据库中读取而不是写入。所以应该可以通过不同的线程并行访问文件的不同部分。即使您的输入在内存中,这也比简单搜索要快。这仍然是 O (n),但取决于它们并行操作的线程数(例如 t),它使用大约 n/t 比较。这意味着它比简单算法快 t 倍。

最后我应该说,小数组的位优化主要是没用的,重点是如何维护一个大文件而不是维护一个小数组。

【讨论】:

    【解决方案3】:

    一般来说,从N个数中找出K个最大的数:

    1. 在 O(N lg N) 时间内对数字进行排序,然后取 K 最大的。如果磁盘上有数十亿个数字,则必须进行外部(磁盘上)排序,例如外部 MergeSort。

    2. 使用容量为 K 的 Min-Heap 并扫描 N 个值。将 K 个最大值保留在堆中,其中最小的值在顶部。运行时间:O(N lg K)。您可以在扫描磁盘中的数字时将最小堆保留在内存中。

    3. 使用选择算法在预期时间 O(N) 内找到第 (N-K) 个最大值。使用 Quicksort 的分区算法的 Quickselect 算法还将对值进行分区,使得 K 个最大值位于第 (N-K) 个最大值的一侧。预期运行时间:O(N)。但是,该选择算法是在内存中的。

    【讨论】:

    • 答案是缺少处理大文件的主要内容,而是提供了一些类似维基百科的信息。这只是一般信息。
    • @SaeedAmiri:在这三点中,我清楚地提到了如何将算法应用于磁盘上的大数据。
    • 我的意思是重点是并行进行,而不仅仅是正常顺序的。
    • @SaeedAmiri:他在 OP 的帖子中哪里说他有多台计算机用于并行处理?并行性不应该是首要关注的问题。创建数据无法放入内存的算法更为重要。
    • OP不用说了,醒醒吧,现在不是1970,连手机都多处理器了。你只是重复了 1970 年的教科书。在另一个答案上阅读我的 cmets。
    猜你喜欢
    • 2020-08-14
    • 2011-04-25
    • 2022-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-22
    • 1970-01-01
    • 2017-01-17
    相关资源
    最近更新 更多