计算十亿数字的中位数

时间:2010-04-03 13:32:33

标签: algorithm distributed-computing

如果您有十亿个数字和一百台计算机,找到这些数字的中位数的最佳方法是什么?

我的一个解决方案是:

  • 在计算机中平均分割集合。
  • 对它们进行排序。
  • 查找每组的中位数。
  • 对中位数进行排序。
  • 从最低到最高的中位数,一次合并两套。

如果我们有m1 < m2 < m3 ...,那么首先合并Set1Set2,在结果集中,我们可以丢弃低于Set12(合并)中位数的所有数字。所以在任何时候我们都有相同大小的集合。顺便说一下,这不能以并行方式完成。有什么想法吗?

25 个答案:

答案 0 :(得分:53)

啊,我的大脑刚开始装备,我现在有一个明智的建议。如果这是一次采访可能为时已晚,但没关系:

机器1应称为“控制机器”,并且为了参数,它以所有数据开始,并以相同的包裹发送给其他99台机器,否则数据在机器之间均匀分布,它将1/99的数据发送给其他每个数据。分区不必相等,只需关闭。

每台其他机器对其数据进行排序,并以有利于首先找到较低值的方式进行排序。因此,例如快速排序,总是先排序分区的下半部分[*]。它尽快将数据以递增的顺序写回控制机器(使用异步IO以便继续排序,并且可能与Nagle一起使用:实验一点)。

控制机器在数据到达时执行99向合并,但丢弃合并的数据,只计算它看到的值的数量。它将中位数计算为十亿分之十亿和十五亿加上的oneth值的平均值。

这是“牛群中最慢的”问题。在分拣机发送了小于中位数的每个值之前,算法无法完成。在这个数据包中,有一个这样的价值非常高的合理机会。因此,一旦数据的初始分区完成,估计的运行时间是排序1/99数据并将其发送回控制计算机的时间的组合,以及控制读取1/2数据的时间。 “组合”介于这些时间的最大值和总和之间,可能接近最大值。

我的直觉是,通过网络发送数据比分类更快(更不用说只选择中位数),它需要是一个非常快速的网络。如果可以假定网络是瞬时的,那么可能是一个更好的前景,例如,如果你有100个内核可以同等访问包含数据的RAM。

由于网络I / O很可能受到约束,因此可能会出现一些技巧,至少对于返回控制计算机的数据而言。例如,代替发送“1,2,3,... 100”,也许分拣机器可以发送意味着“100个值小于101”的消息。然后,控制机器可以执行修改后的合并,其中它找到所有那些顶级值的最小值,然后告诉所有分拣机它是什么,以便它们可以(a)告诉控制机器如何许多值要“计数”在该值之下,并且(b)从该点继续发送已排序的数据。

更一般地说,可能有一个聪明的挑战 - 响应猜测游戏,控制机器可以与99个分拣机一起玩。

这涉及到机器之间的往返行程,这是我简单的第一个版本避免的。我真的不知道如何盲目估计他们的相对表现,并且由于权衡是复杂的,我想有更好的解决方案,而不是我想到的任何事情,假设这是一个真正的问题。

[*]可用堆栈许可 - 如果您没有O(N)额外空间,您可以选择首先执行哪个部分。但是,如果你有足够的额外空间,你可以选择,如果你没有足够的空间,你至少可以使用你所拥有的削减一些角落,通过先做几个分区的小部分。

答案 1 :(得分:51)

sort -g numbers | head -n 500000001 | tail -n 2 | dc -e "1 k ? ? + 2 / p"

答案 2 :(得分:24)

我不想在这里成为逆向投手,但我不相信排序是必需的,我认为任何涉及排序十亿/ 100数字的算法都会变慢。我们在一台计算机上考虑一种算法。

1)从十亿中随机选择1000个值,并使用它们来了解数字的分布,尤其是范围。

2)不是对值进行排序,而是根据刚刚计算的分布将它们分配给存储桶。选择桶的数量以便计算机可以有效地处理它们,但是否则应该尽可能大。存储桶范围应该使得每个存储桶中的值大致相等(这对于算法并不重要,但它有助于提高效率。100,000个存储桶可能是合适的)。请注意每个存储桶中的值的数量。这是一个O(n)过程。

3)找出中位数所在的铲斗范围。这可以通过简单地检查每个桶中的总数来完成。

4)通过检查该桶中的值来查找实际中位数。如果您愿意,可以在这里使用排序,因为您只排序10,000个数字。如果该存储桶中的值的数量很大,那么您可以再次使用此算法,直到您有足够小的数字进行排序。

这种方法通过划分计算机之间的值来平行地并行化。每台计算机将每个存储桶中的总计报告给执行步骤3的“控制”计算机。对于步骤4,每台计算机将相关存储桶中的(已排序)值发送到控制计算机(您也可以同时执行这两种算法,但它可能不值得。)

总进程为O(n),因为如果桶的数量足够大,步骤3和4都是微不足道的。

答案 3 :(得分:11)

对于现代计算机而言,10亿对于任务来说实际上是一项无聊的任务。我们在这里谈论4 GB的4字节整数...... 4 GB ......这是某些智能手机的RAM。

public class Median {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        int[] numbers = new int[1_000_000_000];

        System.out.println("created array after " +  (System.currentTimeMillis() - start) + " ms");

        Random rand = new Random();
        for (int i = 0; i < numbers.length; i++) {
            numbers[i] = rand.nextInt();
        }

        System.out.println("initialized array after " + (System.currentTimeMillis() - start) + " ms");

        Arrays.sort(numbers);

        System.out.println("sorted array after " + (System.currentTimeMillis() - start) + " ms");

        if (numbers.length % 2 == 1) {
            System.out.println("median = " + numbers[numbers.length / 2 - 1]);
        } else {
            int m1 = numbers[numbers.length / 2 - 1];
            int m2 = numbers[numbers.length / 2];
            double m = ((long) m1 + m2) / 2.0;
            System.out.println("median = " + new DecimalFormat("#.#").format(m));
        }
}

我机器上的输出:

created array after 518 ms
initialized array after 10177 ms
sorted array after 102936 ms
median = 19196

所以这在我的机器上在不到两分钟内完成(1:43,其中0:10是生成随机数),使用单个核心,它甚至做了完整的排序。没什么好看的。

对于更大的数字集,这肯定是一项有趣的任务。我只想在此提出一点:10亿是花生。因此,在开始以惊人的简单任务投入复杂的解决方案之前要三思而后;)

答案 4 :(得分:9)

使用t-digestQ-digest等算法可以有效地分配中位数和第99百分位等订单统计信息的估算

使用任一算法,每个节点都会生成一个摘要,该摘要表示本地存储的值的分布。摘要在单个节点收集,合并(有效地对分布求和),然后可以查找中位数或任何其他百分位数。

这种方法由elasticsearch使用,大概是BigQuery(通过QUANTILES函数的描述)。

答案 5 :(得分:5)

这组数字的中位数

2,3,5,7,11,13,67,71,73,79,83,89,97

是67。

这组数字的中位数

2,3,5,7,11,13,67,71,73,79,83,89

是40。

假设问题是大约1,000,000,000个整数(x),其中0&gt; = x <= 2,147,483,647并且OP正在寻找(元素(499,999,999)+元素(500,000,000))/ 2(如果数字被排序) 。 还假设所有100台计算机都是平等的。

使用我的笔记本电脑和GigE ......

我发现我的笔记本电脑可以在1.3秒内对10,000,000个Int32进行排序。所以一个粗略的估计是十亿次数排序需要100 x 1.3秒(2分10秒);)。

估计千兆以太网上40MB文件的单向文件传输是0.32秒。这意味着所有计算机的排序结果将在大约32秒内返回(计算机99在启动后30秒内未获取其文件)。从那里不应该花很长时间丢弃最低的499,999,998数字,加上接下来的2并除以2。

答案 6 :(得分:4)

这可能会给人们带来惊喜,但是如果这些数字是小到足以容纳32位(或更小)的整数 - 只需做一个桶排序!对于任意数量的32位整数,只需要16GB的RAM,并且以O(n)运行,对于合理的n,它应该优于任何分布式系统,例如:十亿。

一旦你有了排序列表,那么挑选中位数是微不足道的。实际上,您不需要构建排序列表,但只需查看存储桶即可。

下面显示了一个简单的实现。仅适用于16位整数,但扩展到32位应该很容易。

#include <stdio.h>
#include <string.h>

int main()
{
    unsigned short buckets[65536];
    int input, n=0, count=0, i;

    // calculate buckets
    memset(buckets, 0, sizeof(buckets));
    while (scanf("%d", &input) != EOF)
    {
        buckets[input & 0xffff]++;
        n++;
    }

    // find median 
    while (count <= n/2)
    {
        count += buckets[i++];
    }

    printf("median: %d\n", i-1);

    return 0;
}

使用带有十亿(10 9 )个数字的文本文件并使用time运行

time ./median < billion

在我的机器1m49.293s上产生运行时间。大多数运行时间也可能是磁盘IO。

答案 7 :(得分:3)

奇怪的是,我认为如果你有足够的计算机,你最好不要使用O(n)中位数查找算法。 (除非你的核心非常非常慢,否则,我只使用一个并使用O(n)中位数查找算法仅用于1e9数字;如果你有1e12,那么这可能不太实用。)< / p>

无论如何,我们假设我们有超过log n核心来处理这个问题,我们不关心功耗,只是快速得到答案。让我们进一步假设这是一台SMP机器,所有数据已经​​加载到内存中。 (例如,Sun的32核机器属于这种类型。)

一个线程将列表盲目地切成相等大小的块并告诉其他M个线程对它们进行排序。那些线程在(n/M) log (n/M)时间努力地这样做。然后,他们不仅返回他们的中位数,而且还返回他们的第25和第75百分位数(如果选择稍微不同的数字,反常的最坏情况会更好)。现在您有4M范围的数据。然后,您可以对这些范围进行排序并在列表中向上工作,直到找到一个数字,如果您丢弃小于或包含该数字的每个范围,您将丢弃一半数据。这是你的中位数的下限。对上限做同样的事情。这需要M log M时间,并且所有内核都必须等待它,所以它真的浪费了M^2 log M潜在的时间。现在你让你的单个线程告诉其他人抛出范围之外的所有数据(你应该在每次传递时丢掉大约一半)并重复 - 这是一个非常快速的操作,因为数据已经被排序。你不应该重复这个时间超过log(n/M)次才能更快地获取剩余数据并在其上使用标准的O(n)中位数查找器。

因此,总复杂度类似于O((n/M) log (n/M) + M^2 log M log (n/M))。因此,如果O(n)M >> log(n/M),这比一个核心上的M^3 log M < n中位数排序要快,这对于您所描述的方案是正确的。

我认为这是一个非常糟糕的主意,因为它效率低,但速度更快。

答案 8 :(得分:2)

这取决于您的数据。最糟糕的情况是它是均匀分布的数字。

在这种情况下,你可以找到O(N)时间的中位数,如下例所示:

假设你的数字是2,7,5,10,1,6,4,4,6,10,4,7,1,8,4,9,9,3,4,3(范围是1 -10)。

我们创建3个桶:1-3,4-7,8-10。请注意,顶部和底部的大小相同。

我们用数字填充桶,计算每个数量,最大值和最小值

  • low(5):2,1,1,3,3,min 1,max 3
  • middle(10):7,5,6,4,4,6,4,7,4,4,min 4,max 7
  • 高(5):10,10,8,9,9,min 8,max 10

平均值落在中间位置,我们忽略其余的

我们创建3个桶:4,5-6,7。低值将从5开始,最大值为3,最高值为8,最小值为8,数量为5。

对于每个数字,我们计算在低水位和高水位,最大值和最小值中落入多少,并保持中间桶。

  • old low(5)
  • low(5):4,4,4,4,4,max 4
  • 中(3):5,6,6
  • high(2):7,7,min 7
  • old high(5)

现在我们可以直接计算中位数:我们有这样的情况

old low    low          middle  high  old high
x x x x x  4 4 4 4 4 4   5 6 6  7 7   x x x x x

所以中位数是4.5。

假设您对分布情况了解不多,可以微调如何定义范围以优化速度。在任何情况下,性能应该与O(N)一致,因为1 + 1/3 + 1/9 ... = 1.5

由于边缘情况,您需要min和max(例如,如果中位数是旧的低和下一个元素的最大值之间的平均值)。

所有这些操作都可以并行化,您可以将1/100数据提供给每台计算机并计算每个节点中的3个桶,然后分配您保留的桶。这再次使您有效地使用网络,因为每个数字平均传递1.5倍(因此O(N))。如果你只在节点之间传递最小数字,你甚至可以击败它(例如,如果节点1有100个数字而节点2有150个数字,那么节点2可以给节点1提供25个数字。)

除非你对分发有更多了解,否则我怀疑你在这里做得比O(N)好,因为你实际上需要至少计算一次这些元素。

答案 9 :(得分:2)

更简单的方法是加权数字。

  • 拆分计算机中的大集
  • 对每一组进行排序
  • 遍历小集,并计算重复元素的权重
  • 将每2组合并为1(每个已经分类)更新权重
  • 保持合并集,直到您只获得一组
  • 迭代此集合累加权重,直到达到OneBillion / 2

答案 10 :(得分:2)

这可以比投票的算法(n log n)更快地完成
  - 订单统计分布式选择算法 - O(n)
将问题简化为在未排序数组中找到第k个数字的原始问题   - 计数排序直方图O(n)
你必须假设一些关于数字范围的属性 - 范围是否适合内存?   - 外部合并排序 - O(n log n) - 如上所述
你基本上对第一遍的数字进行排序,然后在第二遍找到中位数   - 如果对其他数字的分布有任何了解    可以生成算法。

有关详细信息和实施,请参阅:
http://www.fusu.us/2013/07/median-in-large-set-across-1000-servers.html

答案 11 :(得分:2)

一台计算机足以解决问题。

但我们假设有100台电脑。您应该做的唯一复杂事情是对列表进行排序。将它分成100个部分,将一个部件发送到每台计算机,让它们在那里进行分类,然后合并部分。

然后从排序列表的中间取数字(即索引为5 000 000 000)。

答案 12 :(得分:1)

这可以在节点上使用未按节点(例如从日志文件)排序的数据以下列方式完成。

有1个父节点和99个子节点。子节点有两个api调用:

  • stats():返回min,max和count
  • compare(median_guess):返回计数匹配值,count小于value,count大于value

父节点在所有子节点上调用stats(),注意所有节点的最小值和最大值。

现在可以通过以下方式进行二进制搜索:

  1. 将最小和最大四舍五入分开 - 这是中位'猜'
  2. 如果大于计数大于小于计数,请将最小值设置为猜测
  3. 如果大于计数小于小于计数,则将最大值设置为猜测
  4. 如果在最小值和最大值相等时计数是奇数
  5. 如果计数在最大值<=最小值+ guess.match_count时完成 这可以通过以下方式使用未排序数据(例如从日志文件)在节点上完成。
  6. 有1个父节点和99个子节点。子节点有两个api调用:

    • stats():返回min,max和count
    • compare(median_guess):返回计数匹配值,count小于value,count大于value

    父节点在所有子节点上调用stats(),注意所有节点的最小值和最大值。

    现在可以通过以下方式进行二进制搜索:

    1. 将最小和最大四舍五入分开 - 这是中位'猜'
    2. 如果大于计数大于小于计数,请将最小值设置为猜测
    3. 如果大于计数小于小于计数,则将最大值设置为猜测
    4. 如果在最小值和最大值相等时计数是奇数
    5. 如果在最大值时计数结束

      如果可以使用O(N / Mlogn / M)排序预先计​​算stats()和compare(),那么O(N / M)预计算的内存复杂度为O(N)预先计算。然后你可以在常量时间做compare(),所以整个事情(包括预计算)将在O(N / MlogN / M)+ O(logN)中运行

      如果我犯了错误,请告诉我!

答案 13 :(得分:1)

将10 ^ 9个数字拆分为10 ^ 7到每台计算机~80MB。每台计算机对其编号进行排序。然后计算机1合并 - 将自己的数字与来自计算机2,计算机3和4等的数字进行合并...然后计算机1将一半的数字写回2,3或4等。然后,1合并对来自计算机的数字进行排序1,2,3,4,写回来。等等。根据计算机上RAM的大小,您可能无法在每一步将所有数字写回各台计算机,您可以在计算机1上累计数字,但需要进行数学计算。 / p>

哦,最后得到500000000和500000001st值的平均值(但检查那里有足够的00,我没有)。

编辑:@Roman-好吧,如果你不能相信它,即使它是真的,那么我揭露这个命题的真相或谎言是没有意义的。我的意思是,蛮力有时在比赛中击败聪明。我花了大约15秒钟来设计一种算法,我相信我可以实现,这将有效,并且适用于各种尺寸的输入和数量的计算机,并且可以调整计算机的特性和网络安排。如果需要你或其他任何人说15分钟来设计一个更复杂的算法,我有14m45的优势来编写我的解决方案并开始运行。

但我承认这是所有的断言,我没有测量任何东西。

答案 14 :(得分:0)

如果数字不明显,并且只属于某个范围,那就是它们被重复,那么我想到的一个简单的解决方案是将数字平均分配到99台机器中,并将一台机器作为主机。现在,每台机器都迭代其给定的数字,并将每个数字的计数存储在哈希集中。每次在分配给该特定计算机的数字集中重复该数字时,它会更新其在哈希集中的计数。

然后所有机器将其哈希集返回到主机。主机组合散列集,对散列集中找到的相同密钥的计数求和。例如,机器#1的散列集具有(&#34; 1&#34;,7)的条目,并且机器#2的散列集具有(&#34; 1&#34)的条目。 ;,9),因此主机在梳理散列集时会输入(&#34; 1&#34;,16),等等。

一旦哈希集被合并,那么只需对键进行排序,现在您可以从排序的哈希集中轻松找到第(n / 2)项和第(n + 2/2)项。 / p>

如果十亿数字不同,这种方法将不会有益。

答案 15 :(得分:0)

好吧,假设你知道不同整数的数量是(例如)40亿,那么你可以将它们分成64k桶,并从集群中的每台机器(100台计算机)获得每个桶的分布计数。结合所有这些计数。现在,找到具有中位数的桶,这次只询问存储在目标桶中的64k元素的桶。这需要对你的&#34;集群&#34;进行O(1)(特别是2)查询。 :d

答案 16 :(得分:0)

我的便士值得,毕竟已经被其他人提出来了:

在单台机器上查找中位数是O(N):https://en.wikipedia.org/wiki/Selection_algorithm

向100台机器发送N个数字也是O(N)。因此,为了使100台机器变得有趣,要么通信必须相对较快,要么N很大,以至于单个机器无法处理它而N / 100可行,或者我们只想考虑数学问题而不必费心数据通信。

为了缩短时间,我认为在合理的限度内,我们可以发送/分配数字而不影响效率分析。

然后考虑以下方法,其中一台机器被指定为&#34;主机&#34;对于一些一般处理。这将是相对较快的,所以&#34;主人&#34;还参与每台机器执行的常见任务。

  1. 每台机器接收N / 100个数字,计算自己的中位数并将该信息发送给主机。
  2. 主程序编译所有不同中位数的排序列表,并将其发送回每台机器,定义有序的桶序列(在每台机器上相同),每个中值一个(单值桶)和一个用于相邻中位数之间的每个间隔。当然,对于低于最低中位数且高于最高值的值,还有低端和高端桶。
  3. 每台计算机计算每个存储桶中的数量,并将该信息传回主服务器。
  4. 主人确定哪个桶包含中位数,多少个较低值(总计)低于该桶,以及多少个以上。
  5. 如果选定的存储桶是单值存储桶(其中一个中值),则所选存储桶仅包含1(N个奇数)或2个(N个偶数)值。否则,我们将通过以下(显而易见的)修改重复上述步骤:
  6. 只有所选存储桶中的数字(重新)从主服务器分配到100台机器,而且
  7. 我们不打算(在每台机器上)计算中位数,而是计算第k个值,其中我们考虑从总数中丢弃了多少个更高的数字,以及多少个更低的数字。从概念上讲,每台机器也有其丢弃的低/高数字的份额,并在计算集合中的新中位数时考虑到这一点(概念上)包括(它的份额)丢弃的数字。
  8. 时间复杂度:

    1. 一点思考会说服你,在每一步中,要分析的值的总数减少至少两个因子(2将是一个相当恶劣的情况;你可能期望明显更好的减少)。由此我们得到:
    2. 假设找到中位数(或第k个值),即O(N),需要c * N时间,其中前因子c与N的变化不太大,因此我们可以将其作为常数那一刻,我们最终得到的结果是2 * c * N / 100时间。因此,使用100台机器,我们的加速因子为100/2(至少)。
    3. 最初评论说:在机器之间传递数字所涉及的时间可能会使在一台机器上简单地完成所有操作变得更具吸引力。然而,如果我们采用分布式方法,则在所有步骤中一起传送的数字的总数不会超过2 * N(第一次为N,&lt; =第二次为N / 2,&lt; =那第三个,等等。)

答案 17 :(得分:0)

您可以使用锦标赛树方法查找中位数。 我们可以创建一个具有1000个离开节点的树,这样每个叶节点就是一个数组。 然后我们在不同阵列之间进行n / 2场比赛。结果是n / 2比赛结束后的根值。

http://www.geeksforgeeks.org/tournament-tree-and-binary-heap/

答案 18 :(得分:0)

我会这样做:

在开始的所有100个工作中找到最高和最低的数字;每台计算机都有自己查询的数据库/文件;

当找到最高和最低的数字时,一台计算机读取数据,并将每个数字均匀地分配给99的其余部分;数字以相等的间隔分布; (一个可以从-100万到0,另一个 - 从0到1亿等);

在接收号码时,99台计算机中的每台计算机都已对它们进行排序;

然后,很容易找到中位数...看看每台计算机有多少个数字,加上所有数字(有多少数字的总和,而不是数字本身),除以2;计算哪个计算机是数字,以及在哪个索引;

:) voilla

P.S。似乎这里有很多混乱; MEDIAN - 是数字排序列表中间的数字!

答案 19 :(得分:0)

我认为Steve Jessop的答案是最快的。

如果网络数据传输大小是瓶颈,这是另一种方法。

Divide the numbers into 100 computers (10 MB each). 
Loop until we have one element in each list     
    Find the meadian in each of them with quickselect which is O(N) and we are processing in parallel. The lists will be partitioned at the end wrt median.
    Send the medians to a central computer and find the median of medians. Then send the median back to each computer. 
    For each computer, if the overall median that we just computed is smaller than its median, continue in the lower part of the list (it is already partitioned), and if larger in the upper part.
When we have one number in each list, send them to the central computer and find and return the median.

答案 20 :(得分:0)

让我们首先找出如何在一台机器上找到n个数字的中位数: 我基本上使用分区策略。

问题:选择(n,n / 2):从最小数字中找到第n / 2个数字。

您选择说中间元素k并将数据分区为2个子数组。第一个包含所有元素&lt; k和2nd包含所有元素&gt; = k。

如果sizeof(第一个子阵列)&gt; = n / 2,您知道该子数组包含中位数。然后你可以抛弃第二个子阵列。解决此问题选择(第一个子阵列的大小,n / 2)

在其他情况下,抛弃第1个子阵列并解决选择(第2个子阵列,n / 2 - sizeof(第1个子阵列))

递归地做。

时间复杂度 O(n)预期时间。

现在如果我们有很多机器,在每次迭代中,我们必须处理一个数组进行拆分,我们将数组分配到diff机器中。每台机器处理它们的数组块并且将摘要发送回集线器控制机器,即第一个子阵列的大小和第二个子阵列的大小。集线器机器汇总摘要并决定哪个子阵列(第1或第2个)到进一步处理选择的第二个参数并将其发送回每台机器。 等等。

使用map reduce?

可以非常巧妙地实现此算法

它看起来如何?

答案 21 :(得分:0)

这个怎么样: - 每个节点可以占用10亿/ 100个数字。在每个节点处,可以对元素进行排序并且可以找到中值。找出中位数的中位数。我们可以通过在所有节点上汇总小于中位数中位数的数字来找出x%:y%split,其中位数为中位数。现在要求所有节点删除小于中位数中位数的元素(例如30%:70%拆分)。删除30%的数字。 10亿的70%是7亿。现在,删除少于300万个节点的所有节点都可以将这些额外节点发送回主计算机。主计算机以这样的方式重新分配,即现在所有节点将具有几乎相等数量的节点(7百万)。现在问题已减少到7亿个数字....继续,直到我们有一个较小的集合,可以在一个comp上计算。

答案 22 :(得分:-1)

我建议使用一种近似计算中位数的方法。 :)如果这十亿个数字是随机排列的,我想我可以随机选择十亿个数字的1/100或1/10,用100机器对它们进行排序,然后选择它们的中位数。或者让我们分成100个部分的十亿个数字,让每台机器随机挑选每个部分的1/10,计算它们的中位数。之后我们有100个数字,我们可以更容易地计算出100个数字的中位数。只是一个建议,我不确定它是否在数学上是正确的。但我认为你可以把结果显示给一个不那么优秀的数学经理。

答案 23 :(得分:-1)

  1. 将10亿个数字分成100台机器。每台机器将有10 ^ 7个数字。

  2. 对于机器的每个来电号码,请将号码存储在频率图中, 号码 - &gt;计数。还要在每台机器中存储最小号码。

  3. 查找每台机器的中位数:从每台机器的最小数字开始,将计数加总,直到达到中位数指数。每台机器的中位数将是大约。小于和大于5 * 10 ^ 6的数字。

  4. 查找所有中位数的中位数,这将是较小且大于约。 50 * 10 ^ 7个数字,这是10亿个数字的中位数。

  5. 现在对第2步进行一些优化:不是存储在频率图中,而是将计数存储在可变位数组中。例如:假设从机器中的最小数字开始,这些是频率计数:

    [min number] - 8 count
    [min+1 number] - 7 count
    [min+2 number] - 5 count
    

    以上内容可以存储在位数组中:

    [min number] - 10000000
    [min+1 number] - 1000000
    [min+2 number] - 10000
    

    请注意,每台机器总共需要大约10 ^ 7位,因为每台机器只能处理10 ^ 7个数字。 10 ^ 7bits = 1.25 * 10 ^ 6字节,即1.25MB

    因此,使用上述方法,每台机器将需要1.25MB的空间来计算局部中位数。中位数的中位数可以从这100个当地中位数中计算出来,导致中位数为10亿。

答案 24 :(得分:-3)

Steve Jessop的回答是错误的:

考虑以下四组:

{2,4,6,8,10}

{21,21,24,26,28}

{12,14,30,32,34}

{16,18,36,38,40}

中位数为21,包含在第二组中。

四组的中位数分别为6,24,30,36,总中位数为27。

所以在第一个循环之后,这四个组将成为:

{6,8,10}

{24,26,28}

{12,14,30}

{16,18,36}

21已被错误地丢弃。

此算法仅支持有两个组的情况。