10 억 숫자의 중앙값 계산


127

10 억 개의 컴퓨터와 100 대의 컴퓨터가 있다면이 숫자의 중앙값을 찾는 가장 좋은 방법은 무엇입니까?

내가 가진 한 가지 해결책은 다음과 같습니다.

  • 컴퓨터간에 세트를 동일하게 분할하십시오.
  • 그것들을 정렬하십시오.
  • 각 세트의 중앙값을 찾으십시오.
  • 중앙값 세트를 정렬하십시오.
  • 가장 낮은 중앙값에서 가장 높은 중앙값까지 한 번에 두 세트를 병합하십시오.

우리가있는 경우 m1 < m2 < m3 ...먼저 병합을 Set1하고 Set2그 결과 세트에서 우리는 모든 숫자의 평균보다 낮은 삭제할 수 있습니다 Set12(통합). 따라서 어느 시점에서나 동일한 크기의 세트가 있습니다. 그런데 이것은 병렬 방식으로 수행 할 수 없습니다. 어떤 아이디어?


3
@ John Boker : 실제로 문제는 두 가지 하위 문제로 구성됩니다 .1) 목록을 정렬하고 2) 색인이 5'000'000'000 인 요소를 가져옵니다. 나는 숫자가 정렬되어 있다고 믿지 않습니다.
로마

3
@Roman : 문제는 설명하는 두 가지 하위 문제 (예 : quickselect)로 구성 될 필요가 없습니다. 그러나 quickselect는 최소한 사소하지는 않습니다. 물론 숫자가 미리 정렬되어 있으면 그것은 무의미한 질문이라는 것이 맞습니다.
Steve Jessop

5
@fmsf : 저는 영어권 국가가 공식적인 목적으로 수십억 달러 의 영어 를 사용한다고 생각하지 않습니다 . 여기에 영국의 예를 들어, 우리는 내가 "억"의 사용을 고려할 것 1974 년에 사용을 중단 백만 달러를 의미하는 영어로 비뚤어진 트릭 질문, 모든에서 "진짜 억"할 수 있습니다. 물론 프랑스어에서는 전혀 다른 문제가 될 것이지만 질문은 프랑스어가 아닙니다.
Steve Jessop

5
정렬 할 필요가 없습니다! en.wikipedia.org/wiki/…
glebm

2
10 억 개의 숫자는 몇 기가 바이트에 불과하므로이 작업을 해결하기 위해 여러 대의 PC 나 복잡한 알고리즘이 필요하지 않습니다. 지나치게 복잡하지 마십시오.
user626528

답변:


54

아, 내 뇌는 이제 막 시작 됐습니다. 저는 현명한 제안을했습니다. 인터뷰를 한 경우 아마도 너무 늦었지만 신경 쓰지 마십시오.

기계 1은 "제어 기계"라고하며, 논쟁의 여지가 있기 때문에 모든 데이터로 시작하여 같은 소포로 다른 99 대의 기계로 보내거나 데이터가 기계간에 균등하게 분배되기 시작합니다. 데이터의 1/99를 서로에게 보냅니다. 파티션이 같을 필요는 없으며 닫기 만하면됩니다.

서로 다른 컴퓨터는 데이터를 정렬하며 더 낮은 값을 먼저 찾는 것을 선호합니다. 예를 들어, 빠른 정렬은 항상 파티션의 아래쪽을 먼저 정렬합니다 [*]. 데이터는 가능한 한 빨리 순서대로 제어 시스템에 다시 기록합니다 (정렬을 계속하기 위해 비동기 IO 사용, 아마도 Nagle on : 비트 테스트).

제어 시스템은 도착한 데이터에 대해 99-way 병합을 수행하지만, 표시된 값의 수만 유지하면서 병합 된 데이터를 버립니다. 중앙값을 1/2 십억 및 1/2 십억에 1을 더한 평균으로 계산합니다.

이것은 "무리가 가장 느린"문제로 어려움을 겪고 있습니다. 알고리즘은 중간 값보다 작은 모든 값이 정렬 기계에 의해 전송 될 때까지 완료 될 수 없습니다. 그러한 가치 중 하나가 데이터 소포 내에서 상당히 높을 가능성은 합리적입니다. 따라서 데이터의 초기 파티셔닝이 완료되면 예상 실행 시간은 데이터의 1/99를 정렬하여 제어 컴퓨터로 다시 보내는 시간과 컨트롤이 데이터의 1/2을 읽는 시간의 조합입니다. . "조합"은 최대 시간과 그 시간의 합계 사이에있을 수 있으며 아마도 최대에 가깝습니다.

내 본능은 네트워크를 통해 데이터를 전송하는 것보다 데이터를 정렬하는 것보다 빠르기 때문에 (중앙값을 선택하는 것만 제외하고) 상당히 빠른 네트워크 여야한다는 것입니다. 예를 들어 데이터가 포함 된 RAM에 동등한 액세스 권한을 가진 100 개의 코어가있는 경우 네트워크가 즉각적인 것으로 추정 될 수있는 경우 더 나은 전망이 될 수 있습니다.

네트워크 I / O가 한계가 있기 때문에 최소한 데이터가 제어 시스템으로 되돌아 오는 경우 약간의 트릭이있을 수 있습니다. 예를 들어, "1,2,3, .. 100"을 보내는 대신 정렬 시스템에서 "100보다 작은 100 개 값"을 의미하는 메시지를 보낼 수 있습니다. 그런 다음 제어 시스템은 수정 된 병합을 수행 할 수 있습니다. 여기서 병합 된 값 중 가장 작은 값 중 가장 작은 값을 찾은 다음 모든 정렬 시스템에 해당 값을 알려주므로 (a) 제어 시스템에 많은 값이 해당 값 아래로 "계산"되고 (b) 해당 지점에서 정렬 된 데이터 전송을 재개합니다.

보다 일반적으로, 컨트롤 머신이 99 개의 정렬 머신으로 플레이 할 수있는 영리한 도전-응답 추측 게임이있을 것입니다.

그러나 이것은 기계 사이의 왕복 여행과 관련이 있습니다. 단순한 첫 번째 버전은 피합니다. 나는 그들의 상대적인 성과를 맹목적으로 추정하는 방법을 정말로 모른다. 그리고 절충은 복잡하기 때문에, 이것이 실제 문제라고 가정하면, 내가 생각할 것보다 훨씬 더 나은 해결책이 있다고 생각한다.

[*] 사용 가능한 스택 허용-O (N) 추가 공간이없는 경우 먼저 수행 할 부분의 선택이 제한됩니다. 그러나 여분의 공간이 충분하면 선택을 할 수 있고 공간이 충분하지 않으면 처음 몇 개의 파티션에 대해 작은 부분을 먼저 수행하여 모서리를 자르는 데 필요한 것을 사용할 수 있습니다.


내가 틀렸다면 저를 정정하십시오. 왜 나중에 폐기하기 위해 도착했을 때 데이터에 99 방향 병합을 수행합니까? 대신 숫자가 도착할 때 숫자를 유지하는 것으로 충분합니까?
sreeprasad

4
@SREEPRASADGOVINDANKUTTY : 반복 단계는 모든 99 후보 중에서 가장 작은 값을 버리고 카운트를 늘리는 것입니다. 이 99-way 병합 단계없이 모든 들어오는 값의 수를 유지하는 데 전혀 사용되지 않습니다. 들어올 때 비교하지 않으면, 버리는 값이 중앙값 아래에 있다는 것을 모릅니다.
Steve Jessop

그러나 이러한 파티션 중 하나에 중앙값보다 높은 숫자 만 포함되어 있으므로 반환되는 하위 파티션이 중앙값보다 높을 가능성이 적습니다. 중앙값과 실패 ...?
Gullydwarf

@Gullydwarf : 다 방향 병합은 보유하고있는 99 개의 값 중 가장 작은 값만 삭제하며, 각 값은 다른 머신 중 하나에서 가장 작은 나머지 값입니다. 분할 영역 중 하나가 중앙값보다 완전히 크면 중간 값이 지나갈 때까지 (이 시점에서 완료 될 때까지) 99 개 값 중 최소값이되지 않습니다. 따라서 폐기되지 않습니다.
Steve Jessop

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

2
LOL. 그것이 실제로 작동합니까 아니면 OOM 킬러가 완료되기 전에 핵을 생성합니까? (적당한 컴퓨터에서)
Isak Savo

5
해야 할 것. sort는 코어 외부 정렬을 수행하는 방법을 알고 있으므로 메모리가 부족하지 않습니다.
DrPizza

6
@Zagfai 나는 너무 오래 걸릴 것이라고 생각하지 않습니다; 10 억 숫자는 32 비트 int / float의 경우 4GB, 64 비트 int / double의 경우 8GB입니다. 둘 다 과도하게 세금을 부과하는 것 같지 않습니다.
DrPizza

13
인텔 i5-4200M @ 3.1 GHz (4 코어)에서 방금 시도했습니다. time전체 파이프 라인에 적용된 명령에 따르면 real=36m24s( "벽 시계 시간"), user=113m15s ( "병렬 시간", 모든 코어가 추가됨) 소요되었습니다. 다른 명령보다 훨씬 앞선 명령은 sort내 코어에 100 % 스레드 된 경우에도 마찬가지 였습니다 . RAM 소비는 매우 수용 가능했습니다.
Morgan Touverey Quilling

12
그런 다음 100 대의 컴퓨터에서 실행하면 결과가 정확하다는 것을 100 배 더 확실하게 할 수 있습니다 :)
dos

27

나는 여기에 반대되는 것을 싫어하지만 정렬이 필요하다고 생각하지 않으며 10 억 / 100 개의 숫자 정렬과 관련된 알고리즘이 느릴 것이라고 생각합니다. 한 컴퓨터의 알고리즘을 생각해 봅시다.

1) 10 억에서 무작위로 1000 개의 값을 선택하고이를 사용하여 숫자, 특히 범위의 분포에 대한 아이디어를 얻습니다.

2) 값을 정렬하는 대신 방금 계산 한 분포를 기준으로 버킷에 값을 할당하십시오. 버킷 수는 컴퓨터가 효율적으로 처리 할 수 ​​있도록 선택되지만 그렇지 않으면 편리해야합니다. 버킷 범위는 대략 동일한 수의 값이 각 버킷에 들어가도록해야합니다 (알고리즘에는 중요하지 않지만 효율성에 도움이됩니다. 10 만 버킷이 적절할 수 있음). 각 버킷의 값 수를 기록하십시오. 이것은 O (n) 프로세스입니다.

3) 중앙값이 어느 버킷 범위인지 확인하십시오. 각 버킷의 총 수를 간단히 검사하면됩니다.

4) 해당 버킷의 값을 검사하여 실제 중앙값을 찾으십시오. 10,000 개의 숫자 만 정렬하기 때문에 원하는 경우 여기에서 정렬을 사용할 수 있습니다. 해당 버킷의 값 수가 크면 정렬 할만큼 작은 수가 될 때까지이 알고리즘을 다시 사용할 수 있습니다.

이 접근 방식은 컴퓨터간에 값을 나누어 사소하게 병렬화됩니다. 각 컴퓨터는 각 버킷의 총계를 3 단계를 수행하는 '제어'컴퓨터에보고합니다. 4 단계의 경우 각 컴퓨터는 관련 버킷의 (정렬 된) 값을 제어 컴퓨터에 보냅니다 (두 알고리즘 모두 병렬로 수행 할 수 있음) 그러나 가치가 없을 것입니다).

버킷 수가 충분히 많으면 3 단계와 4 단계가 모두 간단하므로 전체 프로세스는 O (n)입니다.


1
나는 이것이 중간 중간 값과 빠른 선택 알고리즘 사이에 있다고 생각합니다. en.wikipedia.org/wiki/Selection_algorithm
Dimath

4 단계에서 버킷에는 10,000 개만 포함될 수 있습니다. 분포가 중간으로 치우친 경우가있을 수 있는데, 여기에는 80 %의 데이터가 포함되어있을 수 있습니다.
justhalf

이를 고려하여 편집했습니다.
DJClayworth

4
이 알고리즘에서 성능은 O (n)이 아닙니다. 대부분의 숫자가 "중앙"버킷에 속할 수 있으며 모든 항목을 정렬하는 것만 큼 성능이 떨어질 수 있습니다.
Sklivvz

1
@WULF 훌륭한 질문입니다. 알고리즘의 핵심이며 1 단계는이를 해결합니다. 분포를 설정하기 위해 숫자를 샘플링하는 것이 내가 찾은 최고입니다.
DJClayworth

12

실제로 10 억은 현대 컴퓨터에서 지루한 작업입니다. 우리는 여기서 4GB 정수의 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은 임의의 숫자를 생성하는) 내 컴퓨터에서 완료되며 전체 정렬을 수행합니다. 정말 멋진 것은 없습니다.

이것은 분명히 더 큰 숫자 집합에 대한 흥미로운 작업입니다. 저는 여기서 지적하고자합니다. 10 억은 땅콩입니다. 놀랍도록 간단한 작업에서 복잡한 솔루션을 던지기 전에 두 번 생각하십시오.)


이것은 내가 여기에 대답 :-)에 말한 stackoverflow.com/a/31819222/363437
vidstige

1
@vidstige 나는 솔직히 그것을 읽지 않았지만, 옳습니다. ) 내 대답은 확실히 더 손에 사람들이 더 많은 비트를 감사하는 것, 이는 비록입니다
sfussenegger

중간하지만 아니에요, 중간는 (numbers[numbers.length / 2]+numbers[numbers.length / 2+1])/2경우 numbers.length도이며 numbers[numbers.length / 2]경우에만 numbers.length홀수입니다.
Sklivvz

@ Sklivvz는 정확하지만 중앙값을 계산하는 데 걸리는 시간에는 눈에 띄지 않아야합니다.
vidstige

1
@Sklivvz 물론입니다. 중간 값 계산을 방금 업데이트했습니다. 그래도 나머지 답변은 변경되지 않습니다.
sfussenegger

10

중간 값 및 99 번째 백분위 수와 같은 차수 통계 의 추정t-digest 또는 Q-digest 와 같은 알고리즘으로 효율적으로 배포 될 수 있습니다 .

각 알고리즘을 사용하여 각 노드는 다이제스트를 생성하여 로컬에 저장된 값의 분포를 나타냅니다. 다이제스트는 단일 노드에서 수집되어 병합 (분포를 효과적으로 합산) 한 다음 중앙값 또는 다른 백분위 수를 찾을 수 있습니다.

이 접근법은 elasticsearch 및 아마도 BigQuery (QUANTILES 함수의 설명으로 이동)에서 사용됩니다.


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 억 개의 숫자 정렬에 100 x 1.3 초 (2 분 10 초)가 소요될 것입니다.

기가비트 이더넷에서 40MB 파일의 단방향 파일 전송 예상치는 .32 초입니다. 이는 모든 컴퓨터에서 정렬 된 결과가 약 32 초 내에 반환됨을 의미합니다 (컴퓨터 99는 시작 후 30 초까지 파일을 얻지 못했습니다). 거기에서 가장 낮은 499,999,998 개의 숫자를 버리고 다음 2를 더하고 2로 나누는 데 시간이 오래 걸리지 않습니다.


3
다운 유권자 의견? 내가 어떻게 더 잘할 수 있는지 이해하는 데 도움이 될 것입니다.
dbasnett

5
나는 다운 유권자가 아니지만 목록 정렬의 최악의 복잡성이 O (n log n)이기 때문에 10 억을 정렬하는 데 10 억을 정렬하는 데 100 배가 걸리지 않습니다. 또한 메모리가 부족하고 디스크에서 정렬을 시작해야 할 때 정렬 속도가 훨씬 느려집니다.
Richard Poole

당신이 올바른 길을 가고 있다고 생각합니다. 목표가 가장 빠른 답변 일 경우 여러 컴퓨터에서 정렬하는 것이 좋습니다. 그러나 목표가 평균 시간이 가장 낮은 경우 자체 검색을 수행하는 각 머신이 더 의미가 있습니다.
Charlie

메모리 문제로 인한 것이 아닌 동일한 요인이 있다고 가정하면 a*(1e7)log(1e7) = 1.3sec=> a = 1.6e-9sec => a*(1e9)log(1e9) ~ 167sec이므로 추정치가 그렇지 않습니다.
bcorso

추정치가 너무 거칩니다. 첫째, 최악의 시나리오 (예 : 일반적으로 사용되는 퀵 정렬)에서 일부 정렬 알고리즘은 o (n ^ 2)로 이동합니다. 둘째, L2 캐시 크기와 비슷한 테스트 데이터 세트를 선택했습니다. 결과가 왜곡됩니다. 셋째, (다른 많은 응답자처럼) "숫자"는 "정수"를 의미한다고 가정합니다. float, double 또는 decimal을 의미 할 수 있으며 성능 특성이 매우 다릅니다.
Sklivvz

5

이것은 사람들을 놀라게 할 수 있지만 숫자가 32 비트 (또는 더 작은) 안에 들어갈 정도로 작은 정수라면 버킷 정렬을하십시오! 32 비트 int에 제한없이 16GB의 램만 필요하며 O (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

정수를 갖는 것이 가정이라고 말하거나 OP가 요구하는 100 대의 컴퓨터를 사용하는 방법을 다루지 않습니다. 한 노드에서 중앙값을 계산할 수 있지만 그 이유를 나타내지 않는 한 "최상의"솔루션이 아닙니다. 숫자의 개수에 따라, 이러한 경우에 확실히 수행하는 경우에도 변화, 기수 정렬 (N) O3 아니다 en.wikipedia.org/wiki/Radix_sort#Efficiency , 그것의 출력 (N, N 로그)
Sklivvz

"정수가 32 비트 정수 안에 들어가기에 충분히 작은 경우"라고 말함으로써 시작합니다 . 기수 정렬은 게시 한 링크에서 명확하게 설명 된대로 일정한 단어 크기 w 에 대해 O (n)입니다 . 여기서 나는 일정한 단어 크기를 32로 가정합니다.
vidstige

1
99 개의 다른 컴퓨터로 수행 한 작업은이 답변과 관련이 없습니다. 피라미드를 형성하거나 화상을 입히기 위해 서로 쌓을 수 있습니다. 아니면 그냥 무시하십시오.
vidstige

3

이상하게도, 충분한 컴퓨터가 있다면 O(n)중간 값 찾기 알고리즘을 사용하는 것보다 정렬하는 것이 좋습니다 . (그러나 코어가 매우 느리게 진행되지 않는 한 하나만 사용하고 O(n)1e9 숫자에 대해서만 중간 값 찾기 알고리즘을 사용합니다 .하지만 1e12가 있으면 실용적이지 않을 수 있습니다.)

어쨌든, 우리가이 문제를 처리하기 위해 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)). 따라서 이것은 and의 O(n)경우 하나의 코어에서 중간 정렬 보다 빠르며 , 이는 앞에서 설명한 시나리오에 해당됩니다.M >> log(n/M)M^3 log M < n

나는 이것이 비효율적이라고 생각 하면 정말 나쁜 생각 이라고 생각 하지만 더 빠릅니다.


o (n / M log (n / M)) = 1 / M o (n (log n-log M)이므로 o (n / M log (n / 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)-위에서 설명한
기본적으로 첫 번째 패스에서 숫자를 정렬 한 다음 두 번째 패스에서 중앙값을 찾습니다.
-숫자 분포에 대해 알려진 것이 있으면 다른 알고리즘을 생성 할 수 있습니다.

자세한 내용 및 구현은 http://www.fusu.us/2013/07/median-in-large-set-across-1000-servers.html을 참조하십시오.


2

한 대의 컴퓨터로 문제를 해결하기에 충분합니다.

그러나 100 대의 컴퓨터가 있다고 가정 해 봅시다. 당신이해야 할 유일한 복잡한 일은 목록을 정렬하는 것입니다. 그것을 100 개의 부분으로 나누고, 각 컴퓨터에 하나의 부분을 보내고, 그것들을 분류하고, 그 후 부분을 병합하십시오.

그런 다음 정렬 된 목록의 중간에서 번호를 가져옵니다 (예 : 색인 5 000 000 000).


3
어쨌든 지금 내 담당자는 꽤 둥글다 :)
Roman

병합은 기껏해야 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). .

우리는 3 개의 버킷을 만듭니다 : 1-3, 4-7, 8-10. 상단과 하단의 크기는 동일합니다.

우리는 양동이에 숫자를 채우고, 각각 얼마나 많은 숫자를 세는지, 최대 및 최소

  • 낮음 (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은 3으로, 최소 8은 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입니다.

분포에 대해 조금 알고 있다고 가정하면 속도를 최적화하기 위해 범위를 정의하는 방법을 미세 조정할 수 있습니다. 어쨌든 1 + 1/3 + 1/9 ... = 1.5이기 때문에 성능은 O (N)과 함께 가야합니다.

에지 사례로 인해 최소값과 최대 값이 필요합니다 (예 : 중간 값이 이전 최대 값과 다음 요소 사이의 평균 인 경우).

이러한 모든 작업을 병렬화 할 수 있으며 각 컴퓨터에 1/100의 데이터를 제공하고 각 노드에서 3 개의 버킷을 계산 한 다음 유지하는 버킷을 배포 할 수 있습니다. 각 번호가 평균 1.5 번 전달되므로 네트워크를 효율적으로 다시 사용할 수 있습니다 (O (N)). 노드간에 최소 숫자 만 전달하면 (예 : 노드 1에 100 개의 숫자가 있고 노드 2에 150 개의 숫자가있는 경우 노드 2가 노드 1에 25 개의 숫자를 줄 수 있음) 이길 수도 있습니다.

분포에 대해 더 많이 알지 못한다면 실제로 요소를 적어도 한 번 계산해야하기 때문에 여기서 O (N)보다 더 잘 할 수 있을지 의심됩니다.


1
모든 숫자가 같을 때 (알고리즘의 경우) 실제 상황이 더 좋지 않습니까? 내가 맞다면, 버킷 중 어느 것도 모든 요소와 함께 중간 양동이와 완전히 채워지지 않을 것입니다. 따라서 매번 모든 요소를 ​​순회하면서 간격의 중간까지 기하 급수적으로 빠르게 진행해야합니다. 나는 그것이 그럴 것이라고 믿는다 O(n log n). 말이 되나요? 그건 그렇고 내가 당신의 아이디어를 좋아
Dici

1
@Dici는 실제로는 아닙니다. 첫째로 최소와 최대를 알고 있기 때문에 "동일한"시나리오를 쉽게 단축 할 수 있습니다. 대답에서 말했듯이 배포를 알면 버킷 선택을 유도 할 수 있습니다. 둘째, 그것은 여전히 걸릴 o(n)+o(n/3)+o(n/9)+...여전히있는 o(n)하지 o(n log n).
Sklivvz

반면에 최악의 시나리오 인 U 자형 분포가있을 수 있습니다. 나는 그것에 대해 조금 생각하고 최악의 경우를 공식화해야하지만 o(n)순진한 파티션으로 그 경우 보다 나빠질 수 있습니다 .
Sklivvz

음, 최소값과 최대 값은 "모두 동일"사례를 매우 쉽게 처리하는 데 도움이됩니다.
Dici

2

더 쉬운 방법은 가중치를 부여하는 것입니다.

  • 컴퓨터간에 큰 세트를 분할
  • 각 세트를 정렬
  • 작은 세트를 반복하고 반복되는 요소의 가중치를 계산합니다.
  • 각 2 개 세트를 1 개 (각각 이미 정렬되어 있음) 업데이트 가중치로 병합
  • 하나의 세트 만 얻을 때까지 세트 병합
  • OneBillion / 2에 도달 할 때까지이 누적 누적 가중치를 반복하십시오.

1

10 ^ 9, 10 ^ 7을 각 컴퓨터에 각각 80MB ~ 80MB로 나눕니다. 각 컴퓨터는 번호를 정렬합니다. 그런 다음 컴퓨터 1은 컴퓨터 2, 컴퓨터 3 및 4 등의 숫자와 자체 숫자를 병합 정렬합니다. 그런 다음 컴퓨터 1은 숫자의 절반을 2, 3-4 등으로 다시 씁니다. 그런 다음 1 병합은 컴퓨터에서 숫자를 정렬합니다. 1,2,3,4는 다시 쓴다. 등등. 컴퓨터의 RAM 크기에 따라 각 단계에서 개별 컴퓨터에 모든 숫자를 다시 쓰지 않아도 될 수 있습니다. 컴퓨터 1의 숫자를 여러 단계 동안 누적 할 수는 있지만 수학을 수행 할 수 있습니다.

오, 마침내 500000000th와 500000001st의 평균을 얻습니다 (그러나 충분한 00이 있는지 확인하십시오.)

편집 : @Roman-글쎄도 믿을 수 없다면 사실의 제안의 진실이나 허위를 밝히는 데 아무런 의미가 없습니다. 내가 말하고자하는 것은 때로 무차별 대결이 때로는 똑똑하게이기는 것입니다. 내가 구현할 수 있다고 확신하는 알고리즘을 고안하는 데 약 15 초가 걸렸으며 작동 할 것이며 광범위한 입력 및 수의 컴퓨터에 적용 가능하며 컴퓨터의 특성에 맞게 조정할 수 있습니다. 네트워킹 준비. 더 복잡한 알고리즘을 고안하는 데 15 분이 걸리면 솔루션을 코딩하고 실행하는 데 14m45s 이점이 있습니다.

그러나 나는 이것이 모든 주장이라고 자유롭게 인정하며, 아무것도 측정하지 않았습니다.


여기서 우리는 모든 숫자를 합치기 만합니다. 우리는 다음과 같은 방법으로 더 나은 방법을 사용할 수있다.
익명

1
@ anony-당신이 당신의 자신의 질문에 대답하는 동안, 나는 솔루션을 코딩하고 테스트하고 완료 할 것입니다. 더 좋은 방법이있을 것으로 기대하지만 때로는 간단한 방법으로 병렬 처리하면 실제로 어려운 문제에 대해 머리를 긁을 수 있습니다.
고성능 마크

정말 7 분 안에 해봤 어 ? 사실이더라도 믿을 수 없습니다. 나는 비슷한 작업을 수행했으며 (대학 과제) 모든 원격 작업을 구현하고 테스트하는 데 약 2 시간이 걸렸습니다 (Java RMI를 사용했습니다).
로마

나는 당신이 무엇을 말하는지 알지만 같은 토큰으로 DrPizza는 더 빠른 생각을 할 수있는 솔루션을 가지고 있습니다.이 솔루션은 모든 데이터를 단일 노드에서 정렬하고 다른 노드를 무시하는 것입니다. 양도를 고려해야하므로 우리는 모호하게 그럴듯한 타협을 선택하고 있습니다. 귀하의 솔루션은 모든 데이터를 여러 번 전송하므로 약간 의심 스럽지만 확실히 솔루션입니다.
Steve Jessop

'모호하게 그럴듯한'-저에게 @Steve 충분합니다! 특히 애매 모호한 질문에 대한 답변.
고성능 마크

1

이는 노드에서 로그 파일 등으로 정렬되지 않은 데이터를 사용하여 다음과 같은 방식으로 노드에서 수행 할 수 있습니다.

1 개의 상위 노드와 99 개의 하위 노드가 있습니다. 자식 노드에는 두 개의 API 호출이 있습니다.

  • stats () : 최소, 최대 및 개수를 반환
  • compare (median_guess) : 개수 일치 값을 반환합니다. 값보다 작고 값보다 큽니다.

부모 노드는 모든 자식 노드에서 stats ()를 호출하여 모든 노드의 최소값과 최대 값을 나타냅니다.

이진 검색은 이제 다음과 같은 방식으로 수행 될 수 있습니다.

  1. 최소 및 최대 반올림 양분-중간 값 '추측'
  2. 보다 큼 개수가보다 작 으면 최소값을 추측으로 설정하십시오.
  3. 보다 큼 개수가보다 작음 개수보다 작 으면 최대 값을 추측 값으로 설정하십시오.
  4. 최소값과 최대 값이 같을 때 카운트가 홀수 인 경우
  5. 최대 <= 최소 + guess.match_count 일 때 카운트가 완료되면 다음과 같은 방식으로 정렬되지 않은 데이터 (로그 파일 등)를 사용하여 노드에서 수행 할 수 있습니다.

1 개의 상위 노드와 99 개의 하위 노드가 있습니다. 자식 노드에는 두 개의 API 호출이 있습니다.

  • stats () : 최소, 최대 및 개수를 반환
  • compare (median_guess) : 개수 일치 값을 반환합니다. 값보다 작고 값보다 큽니다.

부모 노드는 모든 자식 노드에서 stats ()를 호출하여 모든 노드의 최소값과 최대 값을 나타냅니다.

이진 검색은 이제 다음과 같은 방식으로 수행 될 수 있습니다.

  1. 최소 및 최대 반올림 양분-중간 값 '추측'
  2. 보다 큼 개수가보다 작 으면 최소값을 추측으로 설정하십시오.
  3. 보다 큼 개수가보다 작음 개수보다 작 으면 최대 값을 추측 값으로 설정하십시오.
  4. 최소값과 최대 값이 같을 때 카운트가 홀수 인 경우
  5. 최대 <= 최소 + 추측 일 때 카운트가 완료된 경우

stats () 및 compare ()를 O (N / Mlogn / M) 정렬로 미리 계산할 수있는 경우 사전 계산. 그런 다음 일정한 시간에 compare ()를 수행 할 수 있으므로 모든 사전 계산을 포함하여 O (N / MlogN / M) + O (logN)

내가 실수했다면 알려주세요!


그래, 난 그냥 이진 검색을 할 것입니다. 각 컴퓨터에 몇 번만 호출하는 네트워크 대역폭을 절약하십시오. 또한 각 기계에는 "피벗"기능이있어 피벗의 어느 쪽이든 숫자를 교환하여 시간을 절약 할 수 있습니다. (피벗은 중앙값의 이전 추정치이므로 다음 번에는 피벗의 한쪽에있는 모든 숫자 만 통과하면됩니다)
robert king

0

어떻습니까 :-각 노드는 10 억 / 100 개의 숫자를 사용할 수 있습니다. 각 노드에서 요소를 정렬하고 중앙값을 찾을 수 있습니다. 중앙값의 중앙값을 찾습니다. 모든 노드에서 중간 값보다 적은 수의 숫자를 집계하여 중간 값으로 만드는 x % : y % 분할을 찾을 수 있습니다. 이제 모든 노드에 중간 값의 중앙값보다 작은 요소를 삭제하도록 요청하십시오 (예 : 30 % : 70 % 분할) 30 % 숫자가 삭제됩니다. 10 억의 70 %는 700 백만입니다. 이제 3 백만 개 미만의 노드를 삭제 한 모든 노드는 추가 노드를 기본 컴퓨터로 다시 보낼 수 있습니다. 기본 컴퓨터는 이제 모든 노드가 거의 같은 수의 노드 (7 백만)를 갖도록 재분배합니다. 이제 문제는 7 억으로 줄어든다. 하나의 comp에서 계산할 수있는 더 작은 세트를 가질 때까지 계속된다.


본질적으로 우리는 항상 문제 세트를 30 % 이상 줄이고이를 통해 많은 병렬 컴퓨팅을 달성하고 있습니다. 각 노드는 천만에서 시작하여 각 반복에서 데이터 세트를 30 % 줄입니다.
익명

첫 번째 반복에서 500 백만 개의 숫자를 찾습니다. 두 번째 반복에서-삭제 된 숫자의 수가
300million

2
이것은 올바른 궤도에있는 것처럼 보이지만 30 % / 70 %의 분할로 우연히 중앙값을 버리지 않는 방법을 명확하게 설명하지는 않습니다. 다음과 같은 반례를 보자. 처음 29 %가 모두 0이고 다른 모든 블록이 1000으로 계산되고 각 블록 세트가 마지막보다 하나 더 있다고 가정합니다. 30 번째 백분위 수 중앙값은 데이터의 29 %와 데이터의 61 % 중 절반 미만 (29 + 30 % = 59 %)을 버립니다. 죄송합니다. 실제 중앙값을 버렸습니다! 따라서 분명히 당신은 그것을 의미하지는 않습니다.
렉스 커

0

단일 머신에서 n 개의 중간 값을 찾는 방법을 먼저 알아 봅시다. 기본적으로 파티셔닝 전략을 사용하고 있습니다.

문제 : 선택 (n, n / 2) : 최소 숫자에서 n / 2 번째 숫자를 찾습니다.

중간 요소 k를 선택하고 데이터를 2 개의 하위 배열로 분할합니다. 첫 번째는 모든 요소 <k를 포함하고 두 번째는 모든 요소> = k를 포함합니다.

sizeof (1st sub-array)> = n / 2 인 경우이 하위 배열에 중간 값이 포함되어 있음을 알 수 있습니다. 그런 다음 두 번째 하위 배열을 분리 할 수 ​​있습니다. 이 문제 선택을 해결하십시오 (크기의 첫 번째 하위 배열, n / 2) .

그렇지 않으면이 첫 번째 하위 배열을 버리고 선택을 해결하십시오 (2 번째 하위 배열, n / 2-sizeof (1st subarray))

재귀 적으로하십시오.

시간 복잡도는 O (n) 예상 시간입니다.

이제 반복 할 때마다 많은 머신이있는 경우 분할 할 배열을 처리해야하며, 배열을 diff 머신으로 분배합니다. 각 머신은 배열 청크를 처리하고 요약을 허브 제어 머신 (예 : 첫 번째 서브 어레이 크기 및 두 번째 서브 어레이 크기)으로 다시 보냅니다. 허브 머신은 요약을 추가하고 추가 및 두 번째 선택 매개 변수를 처리 할 서브 어레이 (1 차 또는 2 차)를 결정하여 각 머신으로 다시 보냅니다. 등등.

이 알고리즘은 map reduce?를 사용하여 매우 깔끔하게 구현할 수 있습니까?

어떻게 보입니까?


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.

각각 32MB, 의미합니까?
Dici

목록의 아래 부분에서 계속한다는 것은 무엇을 의미합니까?
Ruthvik Vaila

0

나는 이렇게 할 것입니다 :

처음에는 100 개가 모두 가장 높은 숫자와 가장 낮은 숫자를 찾기 위해 노력합니다. 각 컴퓨터는 자신이 쿼리하는 데이터베이스 / 파일의 일부를 가지고 있습니다.

가장 높은 숫자와 가장 낮은 숫자가 발견되면 한 컴퓨터가 데이터를 읽고 각 숫자를 99의 나머지 부분에 균등하게 분배합니다. 숫자는 동일한 간격으로 분배됩니다. (하나는 -100 백만에서 0, 다른 하나는 0-1 억 등이 될 수 있음);

번호를받는 동안 99 대의 컴퓨터 각각은 이미 번호를 정렬합니다.

그런 다음 중앙값을 쉽게 찾을 수 있습니다 ... 각 컴퓨터에 몇 개의 숫자가 있는지 확인하고 모든 숫자를 추가하십시오 (숫자 자체가 아닌 숫자의 합계). 2로 나눕니다. 어느 컴퓨터가 숫자인지, 어느 인덱스인지 계산하십시오.

:) 보일라

추신 : 여기에 많은 혼란이있는 것 같습니다. 중간 값-정렬 된 숫자 목록의 중간 숫자입니다!


0

토너먼트 트리 방법을 사용하여 중앙값을 찾을 수 있습니다. 각 리프 노드가 배열이되도록 1000 개의 휴가 노드가있는 트리를 만들 수 있습니다. 그런 다음 서로 다른 배열간에 n / 2 토너먼트를 진행합니다 .n / 2 토너먼트 이후 루트의 가치가 결과입니다.

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


0

숫자가 구별되지 않고 특정 범위에만 속한다면, 즉 반복됩니다. 제 생각에 간단한 해결책은 99 대의 기계 사이에 숫자를 균등하게 분배하고 한 대의 기계를 마스터로 유지하는 것입니다. 이제 모든 머신은 주어진 숫자를 반복하고 각 숫자의 개수를 해시 세트에 저장합니다. 특정 컴퓨터에 할당 된 일련의 숫자에서 숫자가 반복 될 때마다 해시 세트의 수를 업데이트합니다.

그런 다음 모든 머신은 해시 세트를 마스터 머신으로 리턴합니다. 마스터 머신은 해시 세트에서 찾은 것과 동일한 키의 개수를 합산하여 해시 세트를 결합합니다. 예를 들어 machine # 1의 해시 세트에는 ( "1", 7)의 항목이 있고 machine # 2의 해시 세트에는 ( "1", 9)의 항목이 있으므로 해시 세트를 결합 할 때 마스터 시스템은 ( "1", 16) 등입니다.

해시 세트가 병합되면 키만 정렬하면 정렬 된 해시 세트에서 (n / 2) 번째 항목과 (n + 2 / 2) 번째 항목을 쉽게 찾을 수 있습니다.

이 방법은 수십억의 숫자가 다른 경우 도움이되지 않습니다.


0

음, 고유 정수의 수가 40 억이라고 알고 있다고 가정하면 64k 버킷에 버킷을 넣고 클러스터 (100 컴퓨터)의 각 머신에서 각 버킷에 대한 분산 수를 얻을 수 있습니다. 이 수를 모두 합치십시오. 이제 중앙값이있는 버킷을 찾으십시오. 이번에는 대상 버킷에있는 64k 요소의 버킷 만 요청하십시오. 이를 위해서는 "클러스터"에 대한 O (1) (특히 2) 쿼리가 필요합니다. :디


0

내 페니 가치, 다른 사람들에 의해 이미 제기 된 모든 것 :

단일 시스템에서 중간을 찾는 것은 O (N)이다 https://en.wikipedia.org/wiki/Selection_algorithm .

100 대의 컴퓨터로 N 번호를 보내는 것도 O (N)입니다. 따라서 100 대의 기계를 재미있게 사용하려면 통신 속도가 비교적 빠르거나 N이 너무 커서 N / 100을 수행 할 수있는 동안 한 대의 기계가 처리 할 수 ​​없거나, 신경 쓰지 않고 수학적 문제를 고려하려고합니다 데이터 통신.

일을 짧게 줄이기 위해 합리적인 한계 내에서 효율성 분석에 영향을주지 않고 숫자를 보내거나 배포 할 수 있다고 가정하겠습니다.

그런 다음 한 가지 머신이 일부 일반 처리의 "마스터"로 지정된 다음 접근 방식을 고려하십시오. 이것은 비교적 빠르므로 "마스터"는 각 머신이 수행하는 일반적인 작업에도 참여합니다.

  1. 각 기계는 N / 100의 숫자를 받고 자체 중앙값을 계산하여 해당 정보를 마스터로 보냅니다.
  2. 마스터는 모든 개별 중간 값의 정렬 된 목록을 컴파일하여 각 머신에 다시 전송하여 순서대로 지정된 버킷 순서 (각 머신에서 동일하게), 각 중간 값 (단일 값 버킷) 및 간격 사이에 하나씩 순서를 정합니다. 인접 중앙값. 물론 가장 낮은 중앙값과 가장 높은 값의 값에 대한 하한 및 상한 버킷도 있습니다.
  3. 각 머신은 각 버킷에 몇 개의 숫자가 있는지 계산하고 해당 정보를 다시 마스터에게 전달합니다.
  4. 마스터는 중앙값을 포함하는 버킷, 해당 버킷 아래로 떨어지는 값 (전체) 수 및 그 이상을 결정합니다.
  5. 선택한 버킷이 단일 값 버킷 (중간 값 중 하나)이거나 선택한 버킷에는 1 (N 홀수) 또는 2 (N 짝수) 값만 포함됩니다. 그렇지 않으면 우리는 다음 (명백한) 수정으로 위의 단계를 반복합니다.
  6. 선택한 버킷의 숫자 만 마스터에서 100 대의 컴퓨터로 (재) 배포됩니다.
  7. 우리는 (각 기계에서) 중앙값을 계산하지 않고 k 번째 값을 계산합니다. 여기서 총계에서 버려지는 더 많은 수와 더 적은 수의 수를 고려합니다. 개념적으로 각 기계는 폐기 된 낮은 / 높은 숫자의 점유율을 가지며 폐기 된 숫자를 (개념적으로) 포함하는 (개념적으로) 세트의 새로운 중간 값을 계산할 때이를 고려합니다.

시간 복잡성 :

  1. 조금만 생각하면 각 단계에서 분석 할 총 값의 수가 최소한 2 배 줄어든다는 것을 확신 할 수 있습니다 (2는 다소 병이 될 수 있습니다. 이것으로부터 우리는 다음을 얻습니다.
  2. O (N) 인 중앙값 (또는 k 번째 값)을 찾는 데 c * N 시간이 걸린다고 가정합니다. 최대 2 * c * N / 100 시간 안에 최종 결과를 얻을 수 있습니다. 따라서 100 대의 기계를 사용하면 100/2 (최소)의 속도 향상 요소가 제공됩니다.
  3. 처음에 언급 한 바와 같이, 머신들 사이의 숫자를 전달하는 데 소요되는 시간은 단순히 하나의 머신에서 모든 것을하는 것이 더 매력적일 수 있습니다. 그러나 분산 접근 방식을 사용하는 경우 모든 단계에서 함께 통신 할 총 수는 2 * N (처음으로 N, 두 번째로 <= N / 2, <= 절반)을 초과하지 않습니다. 세 번째 등).

-1
  1. 10 억 개의 숫자를 100 대의 기계로 나눕니다. 각 기계에는 10 ^ 7 개의 숫자가 있습니다.

  2. 기계로 들어오는 각 번호에 대해 번호를 주파수 맵, 숫자-> 개수에 저장하십시오. 또한 각 머신에 최소 번호를 저장하십시오.

  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 개의 기계로 정렬 한 다음 그 중간 값을 선택할 수 있다고 생각합니다. 또는 100 개의 부분으로 10 억 개의 숫자를 나누고, 각 기계가 각 부분의 1/10을 무작위로 선택하고, 그 중간 값을 계산하도록합시다. 그 후 우리는 100 개의 숫자를 가지고 있으며 100 개의 숫자의 중앙값을 더 쉽게 계산할 수 있습니다. 단지 제안, 수학적으로 올바른지 확실하지 않습니다. 그러나 나는 당신이 결과를 좋지 않은 매니저에게 보여줄 수 있다고 생각합니다.


그것은 맞지 않다. 나는 당신의 면접관이 당신이 속일 수있는 멍청한 돼지라고 가정하지 말 것을 강력히 권고한다
Dici

Haha ok, 대답이 틀렸다는 사실을 변경하지는 않습니다. 그것을 증명하는 것은 매우 쉽습니다
Dici

통계에 대한 강의를 읽은 후 10 억 숫자 중 1/100 또는 1/1000을 임의로 선택하여 중앙값을 계산하는 아이디어가 그렇게 나쁘지 않다고 생각합니다. 대략적인 계산 일뿐입니다.
lazyboy 2016 년

-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은 이미 잘못 폐기되었습니다.

이 알고리즘은 두 그룹이있는 경우 만 지원합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.