10億の数値の中央値を計算する


127

10億の数値と100のコンピューターがある場合、これらの数値の中央値を特定する最良の方法は何ですか?

私が持っている1つの解決策は:

  • セットをコンピュータ間で均等に分割します。
  • それらを並べ替えます。
  • 各セットの中央値を見つけます。
  • セットを中央値で並べ替えます。
  • 最小中央値から最大中央値まで、一度に2つのセットをマージします。

我々が持っている場合はm1 < m2 < m3 ...、最初のマージをSet1し、Set2そして得られたセットで、我々はすべての数字が中央値よりも低く破棄することができますSet12(マージ)。したがって、どの時点でも同じサイズのセットがあります。ちなみに、これは並行して行うことはできません。何か案は?


3
@John Boker:実際には、問題は2つのサブ問題で構成されています。1)リストを並べ替え、2)インデックス5'000'000'000の要素を取得します。数字が並べ替えられているとは、私には信じられません。
ローマ

3
@Roman:問題は、記述した2つのサブ問題(たとえば、quickselect)で構成される必要はありません。しかし、quickselectは並列化されません。少なくとも簡単なことではありません。そしてもちろん、あなたが正しいのは、数値が事前に並べ替えられている場合、それはかなり無意味な質問だということです。
スティーブジェソップ2010

5
@fmsf:英語圏の国が公式の目的長い10億の英語を使用しているとは思いません。たとえば、ここイギリスでは、1974年にその使用をやめました。「10億」の使用は、英語で 100万を意味することを意味し、まったく「本当の10億」ではなく、変なトリックの質問だと考えます。もちろんフランス語では、まったく別の問題になりますが、問題はフランス語ではありません。
スティーブジェソップ2010

5
並べ替える必要はありません!en.wikipedia.org/wiki/...
glebm

2
10億の数値は数ギガバイトのデータに過ぎず、このタスクを解決するために複数のPCや複雑なアルゴリズムを必要としません。複雑すぎないでください。
user626528 2013年

回答:


54

ああ、私の脳はちょうどギアに入ったところです、私は今賢明な提案をしています。もしこれがインタビューだったら多分遅すぎますが、気にしないでください:

マシン1は「制御マシン」と呼ばれるものとし、議論のために、すべてのデータから開始し、それを他の99台のマシンに同じ区画で送信するか、データがマシン間で均等に分散されて開始します。 1/99のデータを他のそれぞれに送信します。パーティションは同じでなくてもかまいません。

他の各マシンはそのデータをソートし、低い値を最初に見つけることを優先する方法でソートします。したがって、たとえばクイックソートでは、常にパーティションの下部を最初にソートします[*]。それは、できるだけ早く昇順でデータを制御マシンに書き戻します(ソートを続行するために非同期IOを使用し、おそらくNagleをオンにして:少し実験します)。

制御マシンは、データが到着すると99ウェイマージを実行しますが、マージされたデータは破棄し、検出した値の数を数えます。これは、中央値を2億分の1と1億分の1の値の合計として計算します。

これは「群れの中で最も遅い」問題に悩まされています。アルゴリズムは、中央値より小さいすべての値がソーティングマシンによって送信されるまで完了できません。そのような値の1つが、データのパーセル内で非常に高い可能性があります。したがって、データの初期分割が完了すると、推定実行時間は、データの1/99をソートして制御コンピューターに送信する時間と、コントロールがデータの1/2を読み取る時間の組み合わせになります。 。「組み合わせ」は、最大値とそれらの時間の合計の間のどこかにあり、おそらく最大値に近いでしょう。

私の直感は、ネットワークを介してデータをソートするよりも速く送信するためには(中央値を選択するだけでなく)、かなり高速なネットワークである必要があるということです。ネットワークが瞬間的であると推定できる場合、たとえば、データを含むRAMに同等にアクセスできる100個のコアがある場合は、より良い見通しになる可能性があります。

ネットワークI / Oがバインドされている可能性が高いため、少なくとも制御マシンに戻ってくるデータについて、いくつかのトリックをプレイできる可能性があります。たとえば、「1,2,3、.. 100」を送信する代わりに、ソーティングマシンが「100の値が101未満」を意味するメッセージを送信する可能性があります。次に、制御マシンは、変更されたマージを実行できます。このマージでは、これらの範囲内のすべての値の中で最小のものを見つけ、すべての並べ替えマシンにそれが何であるかを伝え、(a)制御マシンに方法を伝えます。その値より「カウント」する多くの値、および(b)ソートされたデータの送信をそのポイントから再開します。

より一般的には、制御マシンが99台の選別マシンでプレイできる、巧妙なチャレンジ/レスポンス推測ゲームがあるでしょう。

ただし、これにはマシン間の往復が含まれます。これは、私の単純な最初のバージョンでは避けています。私はそれらの相対的なパフォーマンスをブラインド推定する方法を本当に知りません、そしてトレードオフは複雑なので、これが本当の問題であると仮定して、私が自分で考えるよりもはるかに優れたソリューションがあると思います。

[*]利用可能なスタック許可-O(N)の余分なスペースがない場合、最初に実行する部分の選択は制約されます。ただし、十分な追加スペースがある場合はピックを選択できます。十分なスペースがない場合は、最初のいくつかのパーティションに対して最初に小さな部分を実行することで、コーナーを切り取るために必要なものを少なくとも使用できます。


私が間違っている場合は修正してください。データが到着して後で破棄するために、99ウェイマージを実行するのはなぜですか。代わりに、それが到着したときに数を数えるのに十分ですか?
sreeprasad 2014

4
@SREEPRASADGOVINDANKUTTY:繰り返しステップは、99個の候補すべての中で最小の値を破棄し、カウントをインクリメントすることです。この99通りのマージ手順を実行せずに、すべての入力値のカウントを保持するだけではまったく役に立ちません。それらが入ってくるときにそれらを比較しない場合、破棄している値が中央値を下回っていることはわかりません。
スティーブジェソップ2014

しかし、これらのパーティションのいずれかに中央値よりも大きい数値しか含まれていないため、返される下位パーティションが中央値よりも大きくなる可能性は少なくありませんが、コントロールがこれを認識していないため、これらのパーティションが中央値と失敗...?
ガリドワーフ2015

@Gullydwarf:マルチウェイマージは、手元にある99個の値の最小値のみを破棄します。各値は、他のマシンの1つから残っている最小値です。パーティションの1つが中央値よりも完全に大きい場合、中央値が過ぎる(その時点で終了する)までは、これらの99の値の最小値にはなりません。そのため、破棄されません。
Steve Jessop

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

2
笑。それは本当に機能しますか、それともOOMキラーはそれが完了する前にそれを核攻撃しますか?(適切なコンピュータ上で)
Isak Savo 2010年

5
すべきです sortは、コア外ソートを実行する方法を知っているので、メモリが不足することはありません。
DrPizza

6
@Zagfai時間がかかりすぎるとは思いません。10億の数値は、32ビットの整数/浮動小数点数では4 GB、64ビットの整数/倍数整数では8 GBです。どちらも途方もなく重い課税のようです。
DrPizza 2015

13
Intel i5-4200M @ 3.1 GHz(4コア)で試してみました。timeパイプライン全体に適用されたコマンドによると、real=36m24s(「壁時計時間」)、user=113m15s (「並列時間」、すべてのコアが追加された)がかかりました。sort4つのコアを100%スレッド化したとしても、他のコマンドよりはるかに長い最長のコマンドはでした。RAMの消費は非常に許容範囲でした。
モーガントゥーブレイクイリング

11
その後、100台のコンピューターで実行すると、結果が正しいことを100倍確実にすることができます:)
dos

26

ここでは逆張りするのは嫌いですが、並べ替えが必要だとは思いません。10億/ 100の数値の並べ替えを含むアルゴリズムは遅くなると思います。1台のコンピューター上のアルゴリズムを考えてみましょう。

1)10億からランダムに1000個の値を選択し、それらを使用して、数値の分布、特に範囲の分布を把握します。

2)値をソートする代わりに、計算した分布に基づいてバケットに割り当てます。バケットの数は、コンピューターが効率的に処理できるように選択されますが、それ以外の場合は便利な大きさにする必要があります。バケットの範囲は、各バケットにほぼ等しい数の値が入るようにする必要があります(これはアルゴリズムにとって重要ではありませんが、効率を向上させます。100,000バケットが適切な場合があります)。各バケットの値の数に注意してください。これはO(n)プロセスです。

3)中央値がどのバケット範囲にあるかを調べます。これは、各バケットの合計数を調べるだけで実行できます。

4)そのバケットの値を調べて、実際の中央値を見つけます。並べ替えるのはたぶん10,000の数値だけなので、必要に応じてここで並べ替えを使用できます。そのバケットの値の数が多い場合は、並べ替えるのに十分な数になるまで、このアルゴリズムを再度使用できます。

このアプローチは、コンピューター間で値を分割することにより、簡単に並列化します。各コンピューターは、各バケットの合計をステップ3を実行する「制御」コンピューターに報告します。ステップ4の場合、各コンピューターは、関連するバケットの(ソートされた)値を制御コンピューターに送信します(これらのアルゴリズムの両方を同時に実行することもできます。しかし、おそらくそれだけの価値はありません)。

バケットの数が十分に大きければ、ステップ3と4はどちらも簡単なので、全体のプロセスはO(n)です。


1
これは中央値の中央値とクイック選択アルゴリズムの中間にあると思います。 en.wikipedia.org/wiki/Selection_algorithm
Dimath、2013年

ステップ4では、バケットに含まれるのは10,000だけではない場合があります。それは、分布が中央に向かって歪んでいる場合である可能性があり、その場合、たとえば、データの80%が含まれている可能性がありますが、それでもまだ巨大です。
2013年

それを考慮して編集されました。
DJClayworth 2013年

私はこのアプローチが好きです。
Al Kepp、2015

4
このアルゴリズムでは、パフォーマンスはO(n)ではありません。ほとんどの数値が「中央値」バケットに含まれる可能性があり、すべてをソートするのと同じくらいパフォーマンスが悪い可能性があります。
Sklivvz

12

10億は、現代のコンピューターにとって実際には非常に退屈な作業です。ここでは、4 GB相当の4バイト整数について話しています。4GB ...これは一部のスマートフォンの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

そのため、私のマシンでは、シングルコアを使用して2分未満(1:43のうち0:10は乱数を生成する)でこれが完了し、完全なソートも実行しています。派手なものは何もありません。

これは確かに、より大きな数のセットにとって興味深いタスクです。ここで強調したいのは、10億はピーナッツです。したがって、驚くほど単純なタスクで複雑なソリューションを投入する前に、よく考えてください;)


これは私がここで私の回答で言ったことです:-) stackoverflow.com/a/31819222/363437
vidstige

1
@vidstige正直に読みませんでしたが、あなたは正しいです。私の答えは確かにもっと実践的なものですが、人々はもう少し感謝しています;)
sfussenegger

それは中央値ではありませんが、中央値は(numbers[numbers.length / 2]+numbers[numbers.length / 2+1])/2if numbers.lengthが偶数で、奇数のnumbers[numbers.length / 2]場合のみですnumbers.length
Sklivvz

@Sklivvzは正しいですが、中央値の計算にかかる時間に顕著な影響はありません。
vidstige 2015

1
@Sklivvzあなたはもちろん正しいです。中央値の計算を更新しました。それでも答えの残りは変わりません。
sfussenegger、2015

10

中央値や99パーセンタイルなどの順序統計の推定は、t-digestQ-digestなどのアルゴリズムで効率的に分散できます。

いずれかのアルゴリズムを使用して、各ノードはダイジェストを生成します。ダイジェストは、ローカルに保存された値の分布を表します。ダイジェストは単一のノードで収集され、マージされ(実質的には分布を合計し)、中央値または他のパーセンタイルを検索できます。

このアプローチは、で使用されelasticsearchと、おそらく、BigQueryは(変位値関数の説明で行きます)。


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> = x <= 2,147,483,647であり、OPが(element(499,999,999)+ element(500,000,000))/ 2(数値がソートされている場合)を探していたと仮定します。 また、100台すべてのコンピュータがすべて等しいと仮定します。

ラップトップとGigEを使用しています...

私が見つけたのは、私のラップトップが1.3秒で10,000,000個のInt32をソートできることです。したがって、おおよその見積もりでは、10億の数値のソートには10​​0 x 1.3秒(2分10秒)がかかります;)。

ギガビットイーサネットでの40MBファイルの一方向ファイル転送の見積もりは、0.32秒です。これは、すべてのコンピューターからのソートされた結果が約32秒で返されることを意味します(コンピューター99は、開始後30秒までファイルを取得しませんでした)。そこから、最小の499,999,998の数値を破棄し、次の2を追加して2で除算するのに時間がかかることはありません。


3
有権者のコメントを下げる?それは私がもっと上手にできる方法を理解するのに役立ちます。
dbasnett

5
私は反対投票者ではありませんが、リストの並べ替えの最悪の場合の複雑さはO(n log n)であるため、10億の数字の並べ替えは1000万の並べ替えの100倍の時間はかかりません。また、メモリが不足し、ディスクでの並べ替えを開始する必要がある場合、並べ替えの速度は桁違いに遅くなります。
Richard Poole

私はあなたが正しい軌道に乗っていると思います。目標が可能な限り迅速な回答である場合は、複数のマシンでソートすることをお勧めします。しかし、目標が最も低い平均時間である場合は、独自の検索を実行する各マシンがより理にかなっています。
チャーリー

それらが同じ要因を持っていると仮定すると(おそらくメモリの問題が原因ではないでしょう)、a*(1e7)log(1e7) = 1.3sec=> a = 1.6e-9sec => a*(1e9)log(1e9) ~ 167secなので、あなたの見積もりはそれではありませんでした。
bcorso

あなたの見積もりはあまりにもラフです。最初に、一部のソートアルゴリズムは、最悪のシナリオ(たとえば、一般的に使用されるクイックソート)でo(n ^ 2)になります。次に、L2キャッシュとほぼ同じサイズのテストデータセットを選択しました。これは結果を歪めます。第三に、あなたは(他の多くの回答者と同様に)「数」が「整数」を意味すると仮定します。これは、浮動小数点、倍精度、または10進数を意味し、パフォーマンス特性が大きく異なります。
Sklivvz

5

これは人々を驚かせるかもしれませんが、数値が32ビット(またはそれ以下)に収まるほど小さい整数である場合-バケットソートを実行してください!任意の数の32ビット整数で16GBのRAMが必要であり、O(n)で実行されます。これにより、合理的なn、たとえば10億の分散システムよりも優れたパフォーマンスが得られます。

並べ替えられたリストを取得したら、中央値を選択するのは簡単です。実際、ソートされたリストを作成する必要はありませんが、バケットを見るだけで作成できます。

簡単な実装を以下に示します。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億(10 9)の数値を含むテキストファイルを使用して、timeそのように実行する

time ./median < billion

私のマシンでの実行時間は1m49.293sです。実行時間のほとんどは、おそらくディスクIOでもあります。


これは実際には質問に答えるものではなく、仮定に依存しています。たとえば、それらが整数であることさえ知りません。
Sklivvz

どのようにして質問に答えませんか?そして、はい、私の答えは、数値が整数であることを前提としています。私は自分の仮定を明確に述べようとしました。
vidstige 2015

整数を持つことは前提であるとは述べておらず、OPが要求する100台のコンピューターの使用方法についても触れていません。1つのノードの中央値を計算できますが、理由を示さない限り、これは「最良の」ソリューションではありません。桁の数はによると、この場合には確かにいた、変動する場合も、基数ソートO(N)ではありませんen.wikipedia.org/wiki/Radix_sort#Efficiency、それのO(N Nログ)
Sklivvz

まず、「整数が32ビット整数の内部に収まるほど小さい場合」と言います... 投稿したリンクで非常に明確に説明されているように、基数ソートは定数ワードサイズwの O(n)です。ここで私は32の一定のワードサイズと仮定
vidstige

1
他の99台のコンピューターで行うことは、この回答には関係ありません。あなたはそれらを互いの上に積み重ねてピラミッドを形成するか、それらを燃やすことができます。または単にそれらを無視します。
vidstige 2015

3

奇妙なことに、コンピューターが十分にある場合は、O(n)中央値検索アルゴリズムを使用するよりも並べ替えの方が適していると思います。(ただし、コアが非常に遅い場合を除いて、コアを1つ使用し、O(n)中央値検出アルゴリズムを1e9の数値のみに使用します。ただし、1e12がある場合は、あまり実用的ではありません。)

とにかく、この問題に対処するために、log nを超えるコアがあると仮定しましょう。消費電力は気にせず、答えをすばやく得るだけです。さらに、これがSMPマシンであり、すべてのデータがすでにメモリにロードされていると仮定します。(たとえば、Sunの32コアマシンはこのタイプです。)

1つのスレッドがリストを盲目的に同じサイズの断片に切り分け、他の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あなたが説明してきたシナリオの真実です、。

これは非効率的であることを考えると本当に悪い考えだと思いますが、より高速です。


o(n / M log(n / M))は、o(n / M log(n / M))= 1 / M o(n(log n-log M)であるため、文字通りo(n log n)です。 )= o(n log n)。このようなo(n)と実際に比較することはできません。「o」は基本的に「指定されていない定数を持つ非常に大きなnに比例する」ことを意味するためです。これらの定数がわからない場合は比較できませんが、Nが十分に大きい場合、定数は支配的ではありません。数字が小さい場合、すべてのベットがオフになります。o(1)はo(n!)よりも簡単に遅くなる可能性があります。
Sklivvz

@Sklivvz- nMは任意にスケーリングできる変数なので、両方を含めることができます。特に、私はM> を仮定しました。つまりlog n、それn log nが単にnではなく気になる場合は、気にする必要Mもあります。
Rex Kerr

3

これは、投票されたアルゴリズム(n log n)よりも高速に実行できます。-

順序統計分散選択アルゴリズム-O(n)
並べ替えられていない配列でk番目の数を見つけるという問題を単純化します。
-ソートヒストグラムO(n)のカウント
数値の範囲に関するいくつかのプロパティを想定する必要があります-範囲はメモリに収まりますか?-外部マージソート-O(n log n)-上記説明
基本的に、最初のパスで数値をソートし、次に2番目の中央値を見つけます。
-数値の分布について何かわかっている場合は、他のアルゴリズムを生成できます。

詳細と実装については、http//www.fusu.us/2013/07/median-in-large-set-across-1000-servers.htmlを参照して
ください。


2

1台のコンピューターで問題を解決するには十分です。

しかし、100台のコンピュータがあると仮定しましょう。あなたがしなければならない唯一の複雑なことは、リストをソートすることです。それを100の部分に分割し、各コンピューターに1つの部分を送信し、それらをそこで並べ替え、その後で部分をマージします。

次に、ソートされたリストの中央から番号を取得します(つまり、インデックス5 000 000 000)。


3
とにかく今私の担当者はかなり丸いです:)
ローマン

マージはせいぜいO(n)であり、O(n)の単一コアの中央値を見つけることができるので、これは利益がないために多くの追加作業を作成するようです。
Rex Kerr、

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です) 。

1-3、4-7、8-10の3つのバケットを作成します。上部と下部のサイズは同じであることに注意してください。

私たちはバケツに数字を入れ、それぞれにいくつ落ちるか、最大と最小を数えます

  • 低(5):2、1、1、3、3、最小1、最大3
  • 中央(10):7,5,6,4,4,6,4,7,4,4、最小4、最大7
  • 高(5):10、10、8、9、9、最小8、最大10

平均は真ん中のバケツに入ります、残りは無視します

3つのバケットを作成します:4、5-6、7。最低は5のカウントから始まり、最高は3、最高は8、最低は5、カウントは5です。

それぞれの数について、低バケットと高バケット、最大と最小のバケットの数をカウントし、中間のバケットを維持します。

  • 古い低(5)
  • 低(5):4、4、4、4、4、最大4
  • 真ん中(3):5,6,6
  • 高(2):7、7、7分
  • 古い高(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だからです。

エッジケースのために最小値と最大値が必要です(たとえば、中央値が古い低値の最大値と次の要素の平均である場合)。

これらの操作はすべて並列化できます。各コンピューターにデータの1/100を割り当て、各ノードで3つのバケットを計算して、保持するバケットを分散できます。これにより、各数値が平均1.5回渡されるため(O(N))、ネットワークを効率的に使用できます。ノード間で最小限の数のみを渡す場合でも、それを打つことができます(たとえば、ノード1に100の数があり、ノード2に150の数がある場合、ノード2はノード1に25の数を与えることができます)。

分布について詳しく知らない限り、実際には要素を少なくとも1回カウントする必要があるため、ここではO(N)よりも優れているとは思えません。


1
すべての数値が等しい場合、(アルゴリズムの)実際の最悪のケースではありませんか?私が正しければ、すべての要素で真ん中のバケツを除いてバケツが満たされることはありません。したがって、毎回すべての要素をトラバースし、間隔の中央まで指数関数的に速く進む必要があります。私はそれがO(n log n)その場合だと思います。理にかなっていますか?ちなみに私はあなたのアイデアが好きです
ディシ

1
@Diciは実際にはそうではありません。まず、最小と最大を知っているので、「すべて同じ」シナリオを簡単にショートカットできます。回答で述べたように、ディストリビューションを知っていると、バケットの選択が促進される可能性があります。第二に、それはまだかかるだろうo(n)+o(n/3)+o(n/9)+...、まだあるo(n)とありませんo(n log n)
Sklivvz 2015

一方、U字型の分布という別の最悪のシナリオが存在する可能性があります。私はそれについて少し考えて、最悪のケースを形式化する必要がありますo(n)が、素朴なパーティショニングでは、それはその場合よりも悪化する可能性があります。
Sklivvz

うーん、最小と最大は「すべて同じ」のケースをかなり簡単に処理するのに役立ちます
Dici

2

より簡単な方法は、重み付けされた数値を持つことです。

  • 大きなセットをコンピューター間で分割する
  • 各セットを並べ替え
  • 小集合を反復処理し、繰り返される要素の重みを計算します
  • 各2セットを1にマージします(それぞれはすでにソートされています)重みを更新します
  • セットが1つだけになるまでセットをマージし続ける
  • OneBillion / 2に到達するまで、このセットの累積ウェイトを繰り返します

1

10 ^ 9の数値、10 ^ 7を各コンピューターに分割〜それぞれに80MB。各コンピューターはその番号をソートします。次に、コンピュータ1は自身の数値をコンピュータ2、コンピュータ3および4などの数値とマージソートします。次に、コンピュータ1は数値の半分を2、3から4などに書き込みます。次に、1マージはコンピュータの数値をソートします1,2,3,4はそれらを書き戻します。等々。コンピューターのRAMのサイズによっては、各ステップですべての数値を個々のコンピューターに書き戻さなくても済む場合があります。コンピューター1の数値をいくつかのステップで累積できますが、計算は行います。

ああ、最後に500000000番目と500000001番目の値の平均を取得します(ただし、十分な00があることを確認してください)。

編集:@Roman-それが真実であっても信じられない場合は、私の命題の真実または虚偽を明らかにしても意味がありません。私が述べるつもりだったのは、ブルートフォースがレースで時々賢く打ち負かされるということでした。実装できると確信しているアルゴリズムを考案するのに約15秒かかったネットワークの手配。あなたや他の誰かが、より洗練されたアルゴリズムを考案するのに15分かかるとしたら、ソリューションをコード化して実行を開始するのに14分45秒の利点があります。

しかし、私はこれがすべて断言であることを認め、私は何も測定していません。


ここでは、すべての数値をマージソートしています。次のようにして、より良い方法でそれを行うことができますか?「2つのソートされたリストの中央値をlogn時間で見つけることができます。nは各リストの長さです。」
匿名2010

1
@anony-あなたがあなた自身の質問に答えている間、私は私のソリューションをコード化し、テストし、そして完成させます。もっと良い方法があると思いますが、単純な方法を並列化すると、本当に難しい問題に頭を悩ませることができます。
ハイパフォーマンスマーク

あなたは本当にそれを7分でやりましたか?それが本当だとは信じられない。私は同様のタスクを行い(大学の課題でした)、すべてのリモート処理を実装してテストするのに約2時間かかりました(Java RMIを使用しました)。
ローマ

私はあなたの言っていることを理解していますが、同じことを言えば、DrPizzaはさらに迅速に考えられるソリューションを持っています。移管を検討する必要があるため、漠然と考えられる妥協案を選ぶだけです。あなたの解決策はすべてのデータを複数回転送するので、私はそれを少し疑っていますが、それは確かに解決策です。
Steve Jessop、2010

「漠然ともっともらしい」-それは私にとって十分なものです@Steve!特に漠然と信じられない質問に答えて。
ハイパフォーマンスマーク

1

これは、次の方法でノード間で(たとえば、ログファイルから)ソートされていないデータを使用してノードで実行できます。

1つの親ノードと99の子ノードがあります。子ノードには2つのAPI呼び出しがあります。

  • stats():最小、最大、カウントを返します
  • compare(median_guess):値に一致するカウントを返し、カウントが値より小さく、カウントが値より大きい

親ノードはすべての子ノードでstats()を呼び出し、すべてのノードの最小値と最大値を記録します。

バイナリ検索は次の方法で実行できます。

  1. 最小および最大の切り捨てを二等分する-これは中央値の「推測」です
  2. より大きいカウントがより小さいカウントより大きい場合、最小値を推測に設定します
  3. より大きいカウントがより小さいカウントより小さい場合は、最大値を推測に設定します
  4. 最小値と最大値が等しいときにカウントが奇数の場合
  5. 最大値<=最小値+ guess.match_countのときにカウントが終了する場合これは、次の方法で、ソートされていないデータ(たとえば、ログファイルから)を使用してノードで実行できます。

1つの親ノードと99の子ノードがあります。子ノードには2つのAPI呼び出しがあります。

  • stats():最小、最大、カウントを返します
  • compare(median_guess):値に一致するカウントを返し、カウントが値より小さく、カウントが値より大きい

親ノードはすべての子ノードでstats()を呼び出し、すべてのノードの最小値と最大値を記録します。

バイナリ検索は次の方法で実行できます。

  1. 最小および最大の切り捨てを二等分する-これは中央値の「推測」です
  2. より大きいカウントがより小さいカウントより大きい場合、最小値を推測に設定します
  3. より大きいカウントがより小さいカウントより小さい場合は、最大値を推測に設定します
  4. 最小値と最大値が等しいときにカウントが奇数の場合
  5. 最大<=最小+ guess.match_countのときにカウントが終了する場合

stats()とcompare()がO(N / Mlogn / M)ソートで事前計算される可能性がある場合、事前計算のためにO(N)のメモリ複雑度でO(N / M)事前計算計算。次に、compare()を一定の時間で実行できるため、全体(事前計算を含む)はO(N / MlogN / M)+ O(logN)で実行されます

間違えた場合はお知らせください!


ええ、私はバイナリサーチをするだけです。各コンピューターを数回呼び出すだけでネットワーク帯域幅を節約できます。また、各マシンには「ピボット」があり、時間を節約するために、ピボットのいずれかの側で番号を入れ替えます。(ピボットは以前の中央値の推定なので、次回はピボットの片側のすべての数値を通過するだけです)
robert king

0

これはどうですか:-各ノードは10億/ 100の数値を取ることができます。各ノードで要素をソートして中央値を見つけることができます。中央値の中央値を見つけます。すべてのノードで中央値未満の数のカウントを集計することにより、中央値の中央値が行うx%:y%分割を見つけることができます。次に、すべてのノードに中央値の中央値よりも小さい要素を削除するように依頼します(30%:70%の分割を例にとります)。30%の数値が削除されます。10億の70%は7億です。これで、300万個未満のノードを削除したすべてのノードは、それらの追加のノードをメインコンピューターに送信できます。メインコンピュータは、すべてのノードがほぼ同数のノード(700万)を持つように再配布します。これで問題は7億個に減少しました... 1つのコンプで計算できる小さなセットになるまで続きます。


本質的に、私たちは常に設定された問題を少なくとも30%削減しており、これによって多くの並列計算を実現しています。各ノードは1,000万個から始まり、反復ごとにデータセットを30%削減します。
匿名の

最初の反復では、5億分の1の数を探します。2番目の反復では、削除された数の数が3億個の場合、2億個目の数などを探します...
anony

2
これは正しい方向に進んでいるように見えますが、30%/ 70%の分割で誤って中央値を捨てないようにする方法を明確に説明していません。次の反例を見てみましょう。最初の29%がすべてゼロで、他のすべてのブロックが1000ずつカウントアップし、各ブロックのセットが最後のブロックより1つ多いとします。30パーセンタイルの中央値は、データの29%のすべてを破棄し、データの61%の半分をわずかに下回ります。つまり、データの29 + 30%= 59%です。おっと、私たちは真の中央値を捨てました!どうやらあなたはそれを意味するのではなく、少なくとも私が解釈したよりも賢くそれを意味します。
レックスカー

0

最初に、単一のマシンでn個の中央値を見つける方法を考えてみましょう。私は基本的にパーティション化戦略を使用しています。

問題:selection(n、n / 2):最小数からn / 2番目の数を見つけます。

中間要素kを選択し、データを2つのサブ配列に分割します。1番目にはすべての要素<kが含まれ、2番目にはすべての要素> = kが含まれます。

sizeof(1番目のサブ配列)> = n / 2の場合、このサブ配列に中央値が含まれていることがわかります。次に、2番目のサブ配列を破棄できます。この問題の選択を解決してください(sizeof 1st sub-array、n / 2)

それ以外の場合は、この1番目のサブ配列を破棄して、選択を解決します(2番目のサブ配列、n / 2-sizeof(1番目のサブ配列))

再帰的に実行してください。

時間の複雑さは O(n)の予想時間です。

多くのマシンがある場合、各反復で、分割する配列を処理する必要があります。配列をdiffマシンに分散します。各マシンはアレイのチャンクを処理し、サマリーをハブ制御マシンに送信します。つまり、1番目のサブアレイのサイズと2番目のサブアレイのサイズです。ハブマシンは要約を合計し、さらに処理するサブアレイ(1番目または2番目)および選択の2番目のパラメーターを決定し、それを各マシンに送り返します。等々。

このアルゴリズムはmap reduceを使用して非常にきれいに実装できますか?

それはどのように見えますか?


0

スティーブ・ジェソップの答えが一番速いと思います。

ネットワークデータ転送サイズがボトルネックである場合は、別の方法を次に示します。

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.

それぞれ32 MB、つまり?
Dici

リストの下の部分を続けるとはどういう意味ですか?
Ruthvik Vaila

0

私はそれをこのようにします:

最初は、100個すべてが最大数と最小数を見つけるために作業します。各コンピュータには、データベース/ファイルの一部があり、クエリを実行します。

最大値と最小値が見つかると、1台のコンピューターがデータを読み取り、各数値を残りの99に均等に分配します。数値は等間隔で配分されます。(1億から0億、0億から1億など)。

番号を受け取っている間、99台のコンピューターのそれぞれが既に番号をソートしています。

次に、中央値を見つけるのは簡単です...各コンピューターの数を確認し、すべて(それら自体の数ではなく、その数の合計)を追加して、2で割ります。どのコンピューターで、どのインデックスにあるかを計算します。

:)ボイラ

PSここでは多くの混乱があるようです。MEDIAN-ソートされた数のリストの中央にある数です!



0

番号が区別されず、特定の範囲にのみ属している場合、つまり、番号が繰り返されている場合、私の頭に浮かぶ簡単な解決策は、99台のマシンに均等に番号を分配し、1台のマシンをマスターとして維持することです。これで、すべてのマシンが指定された数値を反復処理し、各数値のカウントをハッシュセットに格納します。その特定のコンピュータに割り当てられた一連の番号で番号が繰り返されるたびに、ハッシュセットのカウントが更新されます。

その後、すべてのマシンがハッシュセットをマスターマシンに返します。マスターマシンはハッシュセットを結合し、ハッシュセットで見つかった同じキーの数を合計します。たとえば、machine#1のハッシュセットには( "1"、7)のエントリがあり、machine#2のハッシュセットには( "1"、9)のエントリがあったため、マスターマシンはハッシュセットを組み合わせるときに次のエントリを作成します。 ( "1"、16)など。

ハッシュセットがマージされたら、キーをソートするだけで、ソートされたハッシュセットから(n / 2)番目のアイテムと(n + 2/2)番目のアイテムを簡単に見つけることができます。

この方法は、10億の数値が異なる場合は効果がありません。


0

異なる整数の数が(たとえば)40億であることがわかっているとしたら、それらを64kバケットにバケット化し、クラスター内の各マシン(100コンピューター)から各バケットの分散カウントを取得できます。これらすべてのカウントを組み合わせます。次に、中央値を持つバケットを見つけます。今度は、ターゲットバケットにある64k要素のバケットのみを要求します。これには、「クラスター」に対するO(1)(具体的には2)クエリが必要です。:D


0

私のペニーの価値、結局のところ、それはすでに他の人たちによって育まれてきました:

単一のマシンで中央値を見つけることはO(N):https : //en.wikipedia.org/wiki/Selection_algorithmです。

100台のマシンにN個の番号を送信することもO(N)です。したがって、100台のマシンの使用を面白くするためには、通信が比較的高速であるか、Nが大きすぎて1台のマシンで処理できないためN / 100が実行可能であるか、または問題なく数学的な問題を検討する必要があります。データ通信。

物事を短くするために、合理的な範囲内で、効率分析に影響を与えずに数値を送信/配布できると想定します。

次に、1つのマシンがいくつかの一般的な処理の「マスター」として割り当てられる、次のアプローチを考えます。これは比較的高速であるため、「マスター」も各マシンが実行する一般的なタスクに参加します。

  1. 各マシンはN / 100の数値を受け取り、独自の中央値を計算して、その情報をマスターに送信します。
  2. マスターは、すべての個別の中央値のソートされたリストをコンパイルし、それを各マシンに送り返し、順序付けされたバケットのシーケンス(各マシンで同じ)を定義します。隣接する中央値。もちろん、最低の中央値よりも低く、最高の値よりも高い値のローエンドバケットとハイエンドバケットもあります。
  3. 各マシンは、各バケットに含まれる数を計算し、その情報をマスターに返します。
  4. マスターは、中央値が含まれるバケット、そのバケットを下回る(合計で)いくつの値、およびその上の数を決定します。
  5. 選択したバケットが単一値のバケット(中央値の1つ)である場合、選択したバケットには、1(奇数)または2(偶数)の値しか含まれていません。それ以外の場合は、次の(明らかな)変更を加えて上記の手順を繰り返します。
  6. 選択したバケットの数値のみがマスターから100台のマシンに(再)配布され、さらに
  7. (各マシンで)中央値を計算するのではなく、k番目の値を計算します。ここでは、合計から破棄された高い方の数と低い方の数を考慮します。概念的には、各マシンは廃棄された最小値/最大値のシェアも持っており、(概念的に)廃棄された数値を含む(概念的に)セットの新しい中央値を計算するときにそれを考慮に入れます。

時間の複雑さ:

  1. 少し考えてみると、各ステップで分析する値の総数が少なくとも2分の1に減少していることがわかります(2はかなり悪いケースです。大幅に減少することが予想されます)。これから私達は得る:
  2. O(N)である中央値(またはk番目の値)を見つけるのにc * N時間かかると仮定すると、プリファクターcがNによってあまり大きく変動しないため、当面は定数として取ることができます。せいぜい2 * c * N / 100時間で最終結果が得られます。したがって、100台のマシンを使用すると、スピードアップ係数は100/2(少なくとも)になります。
  3. 最初に述べたように、マシン間で数値をやり取りするのに時間がかかるため、1台のマシンですべてを行うだけの方が魅力的な場合があります。ただし、分散型アプローチを採用する場合、すべてのステップで一緒に通信される数値の合計数は2 * N(最初のN、2回目<= N / 2、その半分以下)を超えません。 3番目など)。

-1
  1. 10億の数値を100台のマシンに分割します。各マシンには10 ^ 7個の数値があります。

  2. マシンへの着信番号ごとに、その番号を頻度マップに保存します(number-> count)。また、各マシンに最小値を保存します。

  3. 各マシンの中央値を見つけます。各マシンの最小値から始めて、中央値インデックスに達するまでカウントを合計します。各マシンの中央値は、約になります。5 * 10 ^ 6よりも大きい、または小さい数値。

  4. すべての中央値の中央値を見つけます。50 * 10 ^ 7の数値。これは10億の数値の中央値です。

次に、第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 ^ 7ビット= 1.25 * 10 ^ 6バイト、つまり1.25MB

したがって、上記のアプローチでは、各マシンはローカル中央値を計算するために1.25MBのスペースを必要とします。そして、中央値の中央値は、それらの100のローカル中央値から計算でき、中央値は10億の数値になります。


数値が浮動小数点数の場合はどうなりますか?
Sklivvz

-1

私はおおよそ中央値を計算する方法を提案します。:)これらの10億の数値がランダムな順序である場合、10億の数値の1/100または1/10をランダムに選択し、100台のマシンでソートしてから、それらの中央値を選択できると思います。または、10進数を100の部分に分割し、各マシンに各部分の1/10をランダムに選択させ、それらの中央値を計算します。その後、100個の数値が得られ、100個の数値の中央値を簡単に計算できます。単なる提案ですが、それが数学的に正しいかどうかはわかりません。しかし、私はあなたがそれほど数学の得意ではないマネージャーに結果を示すことができると思います。


それは明らかに正しくありません。面接担当者があなたが
騙せる

ええ、それはあなたの答えが間違っているという事実を変えることはありませんが。それを証明するのは非常に簡単です
Dici

OK、統計についての講義を読んだ後、1​​0億個のランダムに1/100または1/1000をランダムにピックアップして中央値を計算するという考えはそれほど悪くないと思います。これは単なる概算です。
lazyboy 2015

-3

スティーブ・ジェソップの答えは間違っています:

次の4つのグループを検討してください。

{2、4、6、8、10}

{21、21、24、26、28}

{12、14、30、32、34}

{16、18、36、38、40}

中央値は21で、2番目のグループに含まれています。

4つのグループの中央値は6、24、30、36、合計中央値は27です。

したがって、最初のループの後、4つのグループは次のようになります。

{6、8、10}

{24、26、28}

{12、14、30}

{16、18、36}

21はすでに誤って破棄されています。

このアルゴリズムは、2つのグループがある場合のみサポートします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.