Calcular a mediana de um bilhão de números

127

Se você possui um bilhão de números e cem computadores, qual é a melhor maneira de localizar a mediana desses números?

Uma solução que tenho é:

  • Divida o conjunto igualmente entre os computadores.
  • Classifique-os.
  • Encontre as medianas para cada conjunto.
  • Classifique os conjuntos em medianas.
  • Mesclar dois conjuntos de uma vez da mediana mais baixa à mais alta.

Se tivermos m1 < m2 < m3 ...em seguida, primeira fusão Set1e Set2e no conjunto resultante podemos descartar todos os números mais baixos do que a média de Set12(incorporada). Portanto, a qualquer momento, temos conjuntos de tamanhos iguais. A propósito, isso não pode ser feito de maneira paralela. Alguma ideia?

anonia
fonte
3
@ John Boker: na verdade, o problema consiste em dois subproblemas: 1) classifique a lista e 2) obtenha o elemento com o índice 5'000'000'000. Mal acredito que os números são classificados.
Roman
3
@ Roman: o problema não precisa consistir nos dois subproblemas que você descreve, por exemplo, seleção rápida. Mas a seleção rápida não é paralela, pelo menos não trivialmente. E é claro que você está certo que, se os números são pré-classificados, é uma pergunta bastante inútil.
Steve Jessop
5
@fmsf: Eu não acho que nenhum país de língua inglesa use o longo bilhão em inglês para fins oficiais. Por exemplo, aqui no Reino Unido, paramos de usá-lo em 1974. Eu consideraria o uso de "bilhão" como um milhão de milhões, no idioma inglês uma questão perversa, e não um "bilhão real". É claro que em francês seria uma questão totalmente diferente, mas a questão não está em francês.
21810 Steve Jobs
5
Você não precisa classificar! pt.wikipedia.org/wiki/…
glebm
2
1 bilhão de números é apenas alguns gigabytes de dados, você não precisa de vários PCs nem algoritmos complexos para resolver esta tarefa. Não complique demais.
user626528

Respostas:

54

Ah, meu cérebro acabou de funcionar, tenho uma sugestão sensata agora. Provavelmente tarde demais se tivesse sido uma entrevista, mas não importa:

A máquina 1 deve ser chamada de "máquina de controle" e, por uma questão de argumento, começa com todos os dados e a envia em parcelas iguais às outras 99 máquinas, ou então os dados começam distribuídos igualmente entre as máquinas e envia 1/99 de seus dados para cada um dos outros. As partições não precisam ser iguais, apenas fechadas.

As outras máquinas classificam seus dados e o fazem de uma maneira que favorece encontrar os valores mais baixos primeiro. Por exemplo, uma classificação rápida, sempre classificando a parte inferior da partição primeiro [*]. Ele grava seus dados de volta na máquina de controle em ordem crescente o mais rápido possível (usando E / S assíncronas para continuar classificando e, provavelmente, com Nagle ativado: experimente um pouco).

A máquina de controle executa uma mesclagem de 99 vias nos dados à medida que chegam, mas descarta os dados mesclados, apenas mantendo a contagem do número de valores que viu. Ele calcula a mediana como a média dos valores de 1/2 bilhões e 1/2 bilhões mais oneth.

Isso sofre com o problema "mais lento no rebanho". O algoritmo não pode ser concluído até que todo valor menor que a mediana tenha sido enviado por uma máquina de classificação. Há uma chance razoável de que um desses valores seja bastante alto em sua parcela de dados. Portanto, assim que o particionamento inicial dos dados estiver concluído, o tempo de execução estimado é a combinação do tempo para classificar 1/99 dos dados e enviá-los de volta ao computador de controle, e o tempo para o controle ler 1/2 dos dados . A "combinação" está entre o máximo e a soma desses tempos, provavelmente próximo ao máximo.

Meu instinto é que, para enviar dados através de uma rede para ser mais rápido do que classificá-los (quanto mais para selecionar apenas a mediana), ele precisa ser uma rede muito rápida. Pode ser uma perspectiva melhor se se presume que a rede é instantânea, por exemplo, se você tiver 100 núcleos com acesso igual à RAM contendo os dados.

Como é provável que a E / S da rede seja o limite, pode haver alguns truques que você pode executar, pelo menos para os dados que retornam à máquina de controle. Por exemplo, em vez de enviar "1,2,3, .. 100", talvez uma máquina de classificação possa enviar uma mensagem que significa "100 valores menores que 101". A máquina de controle poderia, então, executar uma mesclagem modificada, na qual encontra o menor de todos esses valores de topo de faixa, e depois informar a todas as máquinas de classificação o que era, para que elas possam (a) dizer à máquina de controle como muitos valores para "contar" abaixo desse valor e (b) retomar o envio dos dados classificados a partir desse ponto.

De um modo mais geral, provavelmente existe um jogo de adivinhação inteligente de resposta a desafios que a máquina de controle pode jogar com as 99 máquinas de classificação.

Isso envolve viagens de ida e volta entre as máquinas, o que minha primeira versão mais simples evita. Realmente não sei como estimar às cegas o desempenho relativo deles, e como as compensações são complexas, imagino que haja soluções muito melhores do que qualquer coisa que eu pense, assumindo que esse seja um problema real.

[*] permissão de pilha disponível - sua escolha de qual parte primeiro será restringida se você não tiver espaço extra de O (N). Mas se você tiver espaço extra suficiente, poderá fazer a sua escolha e, se não tiver espaço suficiente, poderá pelo menos usar o que precisa para cortar alguns cantos, fazendo a pequena parte primeiro nas primeiras partições.

Steve Jessop
fonte
Por favor, corrija-me se estiver errado, por que você está executando a mesclagem de 99 vias nos dados, pois eles chegam apenas para serem descartados mais tarde. Em vez disso, é suficiente manter a contagem dos números à medida que eles chegam?
sreeprasad
4
@SREEPRASADGOVINDANKUTTY: a etapa de repetição é descartar o menor valor de todos os 99 candidatos e aumentar a contagem. Não adianta apenas manter uma contagem de todos os valores recebidos sem essa etapa de mesclagem de 99 vias. Se você não compará-los à medida que entram, não sabe que o valor que está descartando está abaixo da mediana.
9608 Steve Jopp
Mas não há uma pequena chance de que qualquer uma dessas partições contenha apenas números maiores que a mediana e, portanto, qualquer partição menor que ela retornar será maior que a mediana, mas como o controle não sabe disso, as descartará como sendo menores que a mediana e falha ...?
Gullydwarf
@Gullydwarf: uma mesclagem de múltiplas vias descarta apenas o menor dos 99 valores que possui em mãos, cada um dos quais é o menor valor restante de uma das outras máquinas. Se uma das partições for totalmente maior que a mediana, ela não se tornará o menor desses 99 valores até que a mediana tenha passado (nesse ponto, terminamos). Portanto, não será descartado.
21415 Steve Jobs (
52
sort -g numbers | head -n 500000001 | tail -n 2 | dc -e "1 k ? ? + 2 / p"
DrPizza
fonte
2
RI MUITO. Isso realmente funciona ou o assassino da OOM irá destruí-lo antes de concluir? (em qualquer computador razoável)
Isak Savo 28/05
5
Deveria fazer. sort sabe como fazer uma classificação fora do núcleo, para que não fique sem memória.
DrPizza
6
@ Zagfai, acho que não demoraria muito; um bilhão de números é de apenas 4 GB para entradas / flutuantes de 32 bits, 8 GB para entradas / duplas de 64 bits. Nem parece tremendamente desgastante.
DrPizza 3/08
13
Tentei apenas em um Intel i5-4200M a 3,1 GHz (4 núcleos). De acordo com o timecomando aplicado a todo o pipeline, levou real=36m24s("relógio de parede"), user=113m15s ("tempo paralelo", todos os núcleos adicionados). O comando mais longo, bem à frente dos outros, foi sort, mesmo que estivesse ligado aos meus quatro núcleos a 100%. O consumo de RAM foi muito aceitável.
Morgan Touverey Quilling
11
Em seguida, execute em em 100 computadores, para que possa ser 100 vezes mais certeza de que o resultado está correto :)
dos
26

Odeio ser contrário aqui, mas não acredito que a classificação seja necessária, e acho que qualquer algoritmo que envolva a classificação de bilhões / 100 números será lento. Vamos considerar um algoritmo em um computador.

1) Selecione 1000 valores aleatoriamente do bilhão e use-os para ter uma idéia da distribuição dos números, especialmente um intervalo.

2) Em vez de classificar os valores, aloque-os para os baldes com base na distribuição que você acabou de calcular. O número de baldes é escolhido para que o computador possa lidar com eles com eficiência, mas, caso contrário, deve ser o maior possível. Os intervalos de buckets devem ser de modo que números aproximadamente iguais de valores entrem em cada bucket (isso não é crítico para o algoritmo, mas ajuda na eficiência. 100.000 buckets podem ser adequados). Anote o número de valores em cada bloco. Este é um processo O (n).

3) Descubra em qual intervalo de baldes está a mediana. Isso pode ser feito simplesmente examinando o número total em cada bloco.

4) Encontre a mediana real examinando os valores nesse intervalo. Você pode usar uma classificação aqui, se quiser, pois está classificando apenas talvez 10.000 números. Se o número de valores nesse intervalo for grande, você poderá usar esse algoritmo novamente até ter um número pequeno o suficiente para classificar.

Essa abordagem é paralela trivialmente, dividindo os valores entre os computadores. Cada computador relata os totais de cada bloco para um computador 'controle' que executa a etapa 3. Na etapa 4, cada computador envia os valores (classificados) no intervalo relevante para o computador de controle (você também pode executar os dois algoritmos em paralelo, mas provavelmente não vale a pena).

O processo total é O (n), pois as etapas 3 e 4 são triviais, desde que o número de buckets seja grande o suficiente.

DJClayworth
fonte
1
Eu acho que isso é algo entre a mediana de medianas e os algoritmos de seleção rápida. en.wikipedia.org/wiki/Selection_algorithm
Dimath 9/01/13
Na etapa 4, os baldes podem não conter apenas 10.000. Pode ser que a distribuição esteja inclinada para o meio, na qual ela pode conter, digamos, 80% dos dados, o que ainda é enorme.
Just-
Editado para levar isso em conta.
quer
Eu gosto dessa abordagem.
Al Kepp
4
O desempenho não é O (n) neste algoritmo: você pode fazer com que a maioria dos números caia no intervalo "mediano" e pode ter um desempenho tão ruim quanto classificar tudo.
Sklivvz
12

Um bilhão é realmente uma tarefa bastante chata para um computador moderno. Estamos falando de 4 GB de inteiros de 4 bytes aqui ... 4 GB ... essa é a RAM de alguns smartphones.

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));
        }
}

Saída na minha máquina:

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

Portanto, isso é concluído na minha máquina em menos de dois minutos (1:43 dos quais 0:10 são para gerar números aleatórios) usando um único núcleo e até fazendo uma classificação completa. Nada extravagante realmente.

Essa certamente é uma tarefa interessante para conjuntos maiores de números. Eu só quero fazer um ponto aqui: um bilhão é de amendoim. Portanto, pense duas vezes antes de começar a lançar soluções complexas em tarefas surpreendentemente simples;)

sfussenegger
fonte
isto é o que eu disse na minha resposta aqui :-) stackoverflow.com/a/31819222/363437
vidstige
1
@vidstige Eu sinceramente não li, mas você está certo. a minha resposta é certamente mais hands-on, porém, que as pessoas parecem apreciar um pouco mais;)
sfussenegger
Essa não é a mediana, porém, a mediana é (numbers[numbers.length / 2]+numbers[numbers.length / 2+1])/2se numbers.lengthfor par e numbers[numbers.length / 2]somente se numbers.lengthfor ímpar.
Sklivvz
@ Sklivvz correto, mas não deve afetar de maneira perceptível o tempo necessário para calcular a mediana.
vidstige
1
@ Sklivvz você está certo, é claro. Acabei de atualizar o cálculo da mediana. Porém, não muda o resto da resposta.
Sfussenegger 5/08
10

A estimativa de estatísticas de ordem como percentil mediano e 99 podem ser eficientemente distribuída com algoritmos como t-digerir ou Q-digerir .

Usando um algoritmo, cada nó produz um resumo, que representa a distribuição dos valores armazenados localmente. Os resumos são coletados em um único nó, mesclados (somando efetivamente as distribuições) e a mediana ou qualquer outro percentil pode ser consultada.

Essa abordagem é usada pelo elasticsearch e, presumivelmente, pelo BigQuery (seguindo a descrição da função QUANTILES).

Richard Poole
fonte
5

A mediana para esse conjunto de números

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

é 67.

A mediana para esse conjunto de números

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

é 40.

Supondo que a pergunta fosse cerca de 1.000.000.000 de números inteiros (x), em que 0> = x <= 2.147.483.647 e que o OP estava procurando (elemento (499.999.999) + elemento (500.000.000)) / 2 (se os números foram classificados). Também assumindo que todos os 100 computadores eram todos iguais.

usando meu laptop e GigE ...

O que descobri foi que meu laptop pode classificar 10.000.000 de Int32 em 1,3 segundos. Portanto, uma estimativa aproximada seria que uma classificação de número de bilhões levaria 100 x 1,3 segundos (2 minutos e 10 segundos);).

Uma estimativa de uma transferência unidirecional de arquivos de 40 MB em uma Ethernet de gigabit é de 0,32 segundos. Isso significa que os resultados classificados de todos os computadores serão retornados em aproximadamente 32 segundos (o computador 99 não obteve seu arquivo até 30 segundos após o início). A partir daí, não demorará muito para descartar os números mais baixos de 499.999.998, adicione os próximos 2 e divida por 2.

dbasnett
fonte
3
Abaixo o comentário do eleitor? Isso me ajudaria a entender como posso fazer melhor.
#
5
Eu não sou o eleitor que desistiu, mas classificar um bilhão de números não levará 100 vezes mais do que 10 milhões, porque a pior complexidade de classificar uma lista é O (n log n). A classificação também é uma ordem de magnitude mais lenta quando você fica sem memória e precisa começar a classificar no disco.
Richard Poole
Eu acho que você está no caminho certo; Se o objetivo for a resposta mais rápida possível uma vez, classificar em várias máquinas pode ser uma boa ideia. Mas se o objetivo é o menor tempo médio, cada máquina que faz sua própria pesquisa faz mais sentido.
Charlie
Supondo que eles tenham o mesmo fator (o que provavelmente não têm devido a problemas de memória), então a*(1e7)log(1e7) = 1.3sec=> a = 1.6e-9sec => a*(1e9)log(1e9) ~ 167sec, então sua estimativa não foi tão ruim assim.
precisa saber é
Suas estimativas são muito difíceis. Primeiramente, alguns algoritmos de classificação ficam em o (n ^ 2) no pior cenário possível (por exemplo, na quicksort comumente usada). Em segundo lugar, você escolheu um conjunto de dados de teste com o tamanho do cache L2. Isso distorce os resultados. Em terceiro lugar, você (como muitos outros respondentes) assume que "número" significa "número inteiro". Isso pode significar flutuação, dupla ou decimal, com características de desempenho muito diferentes.
Sklivvz
5

Isso pode surpreender as pessoas, mas se os números forem inteiros pequenos o suficiente para caber em 32 bits (ou menores) - basta fazer uma classificação de balde! Precisa apenas de 16 GB de RAM para qualquer número de entradas de 32 bits e é executado em O (n), o que deve superar qualquer sistema distribuído por n razoáveis, por exemplo, um bilhão.

Depois de ter a lista classificada, é trivial escolher a mediana. De fato, você não precisa construir a lista classificada, mas apenas olhando os buckets deve fazê-lo.

Uma implementação simples é mostrada abaixo. Funciona apenas para números inteiros de 16 bits, mas a extensão para 32 bits deve ser fácil.

#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;
}

Usando um arquivo de texto com um bilhão (10 9 ) de números e executando o timemesmo

time ./median < billion

produz um tempo de execução na minha máquina 1m49.293s. A maior parte do tempo de execução provavelmente é de E / S de disco.

vidstige
fonte
Isso realmente não responde à pergunta e se baseia em suposições. Por exemplo, você nem sabe que são números inteiros.
Sklivvz
De que maneira isso não responde à pergunta? E sim, minha resposta assume que os números são inteiros. Eu tentei expor minhas suposições claramente.
vidstige 5/08/2015
Você não parece afirmar que ter números inteiros é uma suposição, nem aborda como usar os 100 computadores sobre os quais o OP pergunta. Você pode calcular a mediana em um nó, mas essa não é a "melhor" solução, a menos que você mostre o porquê. Além disso, a classificação de radix não é o (n) se o número de dígitos variar, o que nesse caso certamente varia, de acordo com en.wikipedia.org/wiki/Radix_sort#Efficiency , é o (n log n)
Sklivvz
Começo dizendo "se os números inteiros forem pequenos o suficiente para caberem dentro de um número inteiro de 32 bits " ... A classificação Radix é O (n) para um tamanho de palavra constante w, conforme descrito em grande clareza no link que você postou. Aqui eu assumir um tamanho constante palavra de 32.
vidstige
1
O que você faz com os outros 99 computadores não é relevante nesta resposta. Você pode empilhá-las umas sobre as outras para formar uma pirâmide ou queimá-las. Ou apenas ignorá-los.
vidstige
3

Por incrível que pareça, se você tiver computadores suficientes, é melhor classificar do que usar O(n)algoritmos de descoberta de mediana. (A menos que seus núcleos sejam muito, muito lentos, basta usar um e usar um O(n)algoritmo de busca mediana para apenas números 1e9; se você tiver 1e12, isso pode ser menos prático).

De qualquer forma, vamos supor que temos mais do que log n núcleos para lidar com esse problema e não nos importamos com o consumo de energia, apenas obtendo a resposta rapidamente. Vamos supor ainda que esta é uma máquina SMP com todos os dados já carregados na memória. (As máquinas de 32 núcleos da Sun são desse tipo, por exemplo.)

Um segmento divide a lista às cegas em pedaços de tamanhos iguais e diz aos outros segmentos M para classificá-los. Esses tópicos diligentemente fazem isso com o (n/M) log (n/M)tempo. Eles retornam não apenas suas medianas, mas, digamos, seus percentis 25 e 75 (os piores casos perversos são melhores se você escolher números ligeiramente diferentes). Agora você tem 4 milhões de faixas de dados. Você classifica esses intervalos e trabalha para cima na lista até encontrar um número tal que, se você jogar fora todos os intervalos menores ou que contenham o número, você jogará metade dos seus dados. Esse é o seu limite inferior para a mediana. Faça o mesmo para o limite superior. Isso leva algo como M log Mtempo, e todos os núcleos precisam esperar por isso, então está realmente desperdiçandoM^2 log Mtempo potencial. Agora você tem seu único thread dizendo aos outros para lançar todos os dados fora do intervalo (você deve jogar cerca de metade em cada passagem) e repetir - esta é uma operação trivialmente rápida, pois os dados já estão classificados. Você não deve repetir isso mais do que o log(n/M)tempo antes que seja mais rápido capturar os dados restantes e usar um O(n)localizador mediano padrão .

Então, complexidade total é algo parecido O((n/M) log (n/M) + M^2 log M log (n/M)). Portanto, isso é mais rápido que a O(n)classificação média em um núcleo se M >> log(n/M)e M^3 log M < n, o que é verdade para o cenário que você descreveu.

Penso que é uma péssima ideia, dado o quão ineficiente é, mas é mais rápido.

Rex Kerr
fonte
o (n / M log (n / M)) é, literalmente, o (n log n), porque o (n / M log (n / M)) = 1 / M o (n (log n - log M) ) = o (n log n). Você não pode realmente compará-lo com o (n) assim, pois o "o" significa basicamente "proporcional a muito grande n com alguma constante não especificada". A menos que você conheça essas constantes, não é possível comparar, no entanto, para N suficientemente grande, as constantes não são dominantes. Para números mais baixos, todas as apostas estão desativadas, o (1) pode ser mais lento que o (n!).
Sklivvz
@ Sklivvz - ne Msão as variáveis ​​que podem ser escalonadas arbitrariamente, então uma inclui as duas. Em particular, eu postulei isso M> log n, o que significa que se você se importa com isso, em n log nvez de apenas n, você também precisa se preocupar M.
Rex Kerr #
3

Isso pode ser feito mais rapidamente do que o algoritmo votado (n log n)

- Algoritmo de seleção distribuído de estatísticas da ordem - O (n)
Simplifique o problema ao problema original de encontrar o número k em um array não classificado.
- Contando o histograma de classificação O (n)
Você deve assumir algumas propriedades sobre o intervalo dos números - o intervalo pode caber na memória? - Classificação de mesclagem externa - O (n log n) - descrito acima
Você basicamente classifica os números na primeira passagem e encontra a mediana na segunda.
- Se alguma coisa for conhecida sobre a distribuição dos números, outros algoritmos poderão ser produzidos.

Para obter mais detalhes e implementação, consulte:
http://www.fusu.us/2013/07/median-in-large-set-across-1000-servers.html

user1712376
fonte
2

Um computador é mais que suficiente para resolver o problema.

Mas vamos assumir que existem 100 computadores. A única coisa complexa que você deve fazer é classificar a lista. Divida-o em 100 partes, envie uma parte para cada computador, deixe-as classificadas lá e mescle-as depois disso.

Em seguida, pegue o número no meio da lista classificada (ou seja, com o índice 5 000 000 000).

romano
fonte
3
De qualquer forma agora meu representante é bastante rodada :)
Roman
A mesclagem é, na melhor das hipóteses, O (n), e você pode encontrar a mediana em um único núcleo em O (n), portanto, isso parece criar muito trabalho extra sem ganho.
Rex Kerr
2

Depende dos seus dados. O pior cenário é que são números distribuídos uniformemente.

Nesse caso, você pode encontrar a mediana no tempo O (N) como neste exemplo:

Suponha que seus números sejam 2,7,5,10,1,6,4,4,6,10,4,7,1,8,4,9,9,3,4,3 (o intervalo é de 1 a 10) .

Criamos 3 baldes: 1-3, 4-7, 8-10. Observe que as partes superior e inferior têm o mesmo tamanho.

Enchemos os baldes com os números, contamos quantas caem em cada um, o máximo e o mínimo

  • baixo (5): 2,1,1,3,3, min 1, max 3
  • middle (10): 7,5,6,4,4,6,4,7,4,4, mínimo 4, máximo 7
  • alto (5): 10, 10, 8, 9, 9, mínimo 8, máximo 10

A média cai no balde do meio, desconsideramos o resto

Criamos 3 buckets: 4, 5-6, 7. Low começará com uma contagem de 5 e com um máximo de 3 e alto com um mínimo de 8 e uma contagem de 5.

Para cada número, contamos quantas caem no balde baixo e alto, o máximo e o mínimo, e mantemos o balde do meio.

  • baixo (5)
  • baixo (5): 4, 4, 4, 4, 4, máximo 4
  • médio (3): 5,6,6
  • alto (2): 7, 7, min 7
  • alto (5)

Agora podemos calcular a mediana diretamente: temos uma situação como esta

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

então a mediana é 4.5.

Supondo que você conheça um pouco da distribuição, pode ajustar como definir os intervalos para otimizar a velocidade. Em qualquer caso, o desempenho deve ser com O (N), porque 1 + 1/3 + 1/9 ... = 1,5

Você precisa de min e max devido a casos extremos (por exemplo, se a mediana for a média entre o máximo da baixa mais antiga e o próximo elemento).

Todas essas operações podem ser paralelizadas, você pode fornecer 1/100 dos dados para cada computador, calcular os 3 intervalos em cada nó e distribuir o intervalo que você mantém. Isso novamente faz com que você use a rede com eficiência, pois cada número é passado em média 1,5 vezes (então O (N)). Você pode até superar isso se você passar apenas os números mínimos entre os nós (por exemplo, se o nó 1 tiver 100 números e o nó 2 tiver 150 números, o nó 2 poderá fornecer 25 números ao nó 1).

A menos que você saiba mais sobre a distribuição, duvido que você possa fazer melhor que O (N) aqui, porque na verdade você precisa contar os elementos pelo menos uma vez.

Sklivvz
fonte
1
Não é realmente o pior caso (para o seu algoritmo) quando todos os números são iguais? Se eu estiver correto, nenhum dos seus baldes será preenchido além do meio, com todos os elementos. Assim, você terá que percorrer todos os elementos de cada vez, progredindo exponencialmente rápido até o meio do intervalo. Eu acredito que seria um O(n log n)nesse caso. Isso faz sentido ? Pelo jeito que eu gosto da sua ideia
Dici
1
@ Dici não realmente: em primeiro lugar, você pode facilmente atalho o cenário "tudo a mesma coisa", porque você sabe min e max. Como eu disse na resposta, saber que a distribuição pode conduzir suas escolhas de balde; segundo, ainda seria preciso o o(n)+o(n/3)+o(n/9)+...que é imóvel o(n)e o que não é o(n log n).
Sklivvz
Por outro lado, provavelmente há um cenário de pior caso diferente, uma distribuição em forma de U. Eu preciso pensar um pouco sobre isso, formalizar o pior caso, mas poderia ser pior do que o(n)nesse caso, com o particionamento ingênuo.
Sklivvz 5/08/2015
Mmm, sim, o min e max ajudaria a lidar com o "tudo mesmo" caso muito facilmente
Dici
2

Um método mais fácil é ter números ponderados.

  • Dividir o conjunto grande entre computadores
  • Classificar cada conjunto
  • iterar no conjunto pequeno e calcular pesos para elementos repetidos
  • mesclar cada 2 conjuntos em 1 (cada um já está classificado) atualizando pesos
  • continue mesclando conjuntos até obter apenas um conjunto
  • itere através deste conjunto acumulando pesos até chegar a OneBillion / 2
Ziad Nasser
fonte
1

Divida os números 10 ^ 9, 10 ^ 7 para cada computador ~ 80 MB em cada um. Cada computador classifica seus números. Então o computador 1 mescla seus próprios números com os do computador 2, computador 3 e 4, etc ... Em seguida, o computador 1 grava metade dos números em 2, 3 a 4, etc. Em seguida, 1 mescla classifica os números dos computadores 1,2,3,4, escreve-os de volta. E assim por diante. Dependendo do tamanho da RAM nos computadores, você pode não escrever todos os números nos computadores individuais a cada etapa, poderá acumular os números no computador 1 por várias etapas, mas faça as contas.

Oh, finalmente obtenha a média dos valores 500000000 e 500000001st (mas verifique se há 00s suficientes lá, não tenho).

EDIT: @Roman - bem, se você não pode acreditar, mesmo que seja verdade, então não faz sentido revelar a verdade ou a falsidade da proposição. O que eu pretendia afirmar era que a força bruta às vezes é inteligente em uma corrida. Demorei cerca de 15 segundos para criar um algoritmo que estou confiante de que posso implementar, que funcionará e que será adaptável a uma ampla variedade de tamanhos de entradas e números de computadores, e sintonizável com as características dos computadores e arranjos de rede. Se você ou qualquer outra pessoa demorar 15 minutos para criar um algoritmo mais sofisticado, tenho uma vantagem de 14m45s para codificar minha solução e iniciá-la em execução.

Mas admito livremente que tudo isso é afirmação, não medi nada.

Marca de alto desempenho
fonte
aqui estamos apenas mesclando todos os números. Podemos fazê-lo de uma maneira melhor usando: - "podemos encontrar a mediana de duas listas classificadas em tempo de logon. N é o comprimento de cada lista."
anony
1
@ anony - enquanto você responde sua própria pergunta, terei minha solução codificada, testada e pronta. Espero que haja maneiras melhores, mas, às vezes, paralelizar uma maneira simples me deixa livre para coçar a cabeça sobre os problemas realmente difíceis.
High Performance Mark
você realmente fez isso em 7 minutos? Não acredito nisso, mesmo que seja verdade. Fiz a tarefa semelhante (era uma tarefa da universidade) e demorou cerca de duas horas para implementar e testar todo o material de comunicação remota (usei java RMI).
Roman
Entendo o que você está dizendo, mas, da mesma forma, o DrPizza tem uma solução ainda mais rápida de se pensar, que é classificar todos os dados em um único nó e ignorar os outros 99. Nenhum de nós sabe o quanto os dados são caros transferência deve ser considerada, então estamos apenas escolhendo um compromisso que parece vagamente plausível. Sua solução transfere todos os dados várias vezes, então desconfio um pouco, mas certamente é uma solução.
21710 Steve Jobs
'vagamente plausível' - isso é bom o suficiente para mim @Steve! Especialmente em resposta a uma pergunta vagamente implausível.
High Performance Mark
1

Isso pode ser feito em nós usando dados que não são classificados entre nós (por exemplo, dos arquivos de log) da seguinte maneira.

Há um nó pai e 99 nós filhos. Os nós filhos têm duas chamadas de API:

  • stats (): retorna min, max e count
  • compare (median_guess): retorna o valor correspondente da contagem, conta menos que o valor e conta maior que o valor

O nó pai chama stats () em todos os nós filhos, observando o mínimo e o máximo de todos os nós.

Uma pesquisa binária agora pode ser realizada da seguinte maneira:

  1. Divida o arredondamento mínimo e máximo - este é o 'palpite' mediano
  2. Se a maior que a contagem for maior que a menor que a contagem, defina o mínimo para a estimativa
  3. Se a contagem maior que for menor que a contagem menor, configure o máximo para a estimativa
  4. Se a contagem for ímpar, termine quando mínimo e máximo são iguais
  5. Se a contagem for concluída mesmo quando máximo <= mínimo + palpite.match_count Isso pode ser feito em nós usando dados não classificados (digamos, de arquivos de log) da seguinte maneira.

Há um nó pai e 99 nós filhos. Os nós filhos têm duas chamadas de API:

  • stats (): retorna min, max e count
  • compare (median_guess): retorna o valor correspondente da contagem, conta menos que o valor e conta maior que o valor

O nó pai chama stats () em todos os nós filhos, observando o mínimo e o máximo de todos os nós.

Uma pesquisa binária agora pode ser realizada da seguinte maneira:

  1. Divida o arredondamento mínimo e máximo - este é o 'palpite' mediano
  2. Se a maior que a contagem for maior que a menor que a contagem, defina o mínimo para a estimativa
  3. Se a contagem maior que for menor que a contagem menor, configure o máximo para a estimativa
  4. Se a contagem for ímpar, termine quando mínimo e máximo são iguais
  5. Se a contagem for finalizada quando máximo <= mínimo + palpite.match_count

Se as estatísticas () e compare () puderem ser pré-calculadas com uma classificação O (N / Mlogn / M), um pré-cálculo O (N / M) com uma complexidade de memória de O (N) para o período pré- Cálculo. Então você pode comparar () em tempo constante, para que tudo (incluindo pré-cálculo) seja executado em O (N / MlogN / M) + O (logN)

Deixe-me saber se eu cometi um erro!

teambob
fonte
Sim, eu apenas faria pesquisa binária. Economizaria largura de banda de rede apenas chamando cada computador algumas vezes. Além disso, cada máquina pode ter um "pivô" onde, no lugar, troca números de ambos os lados do pivô para economizar tempo. (pivot seria a estimativa anterior da mediana, por isso, da próxima vez, só tem que passar por todos os números de um lado do pivô)
Robert King
0

Que tal isso: - cada nó pode levar 1 bilhão / 100 números. Em cada nó, os elementos podem ser classificados e a mediana pode ser encontrada. Encontre a mediana das medianas. Ao agregar as contagens de números abaixo da mediana da mediana em todos os nós, podemos descobrir a divisão x%: y% que a mediana da mediana faz. Agora peça a todos os nós que excluam elementos abaixo da mediana das medianas (por exemplo, 30%: divisão de 70%). Os números de 30% são excluídos. 70% de 1 bilhão é de 700 milhões. Agora todos os nós que excluíram menos de 3 milhões de nós podem enviar esses nós extras de volta para o computador principal. O computador principal é redistribuído de forma que agora todos os nós tenham um número quase igual de nós (7 milhões). Agora que o problema foi reduzido para 700 milhões de números ... continua até termos um conjunto menor que pode ser calculado em uma única composição.

anonia
fonte
Em essência, estamos sempre reduzindo o problema definido em pelo menos 30% e estamos conseguindo muita computação paralela com isso. Cada nó começa com 10 milhões e reduz seu conjunto de dados em 30% em cada iteração.
Anony3 /
Na primeira iteração, procuramos o número 500Milhões de milhões. Na segunda iteração - se o número de números apagados é 300 milhões, em seguida, nós olhamos para o número 200millionth e assim por diante ...
anony
2
Parece que está no caminho certo, mas você não explica muito claramente como evitar jogar fora a mediana por acidente com sua divisão de 30% / 70%. Tome o seguinte contraexemplo: suponha que seus primeiros 29% sejam todos zeros e todos os outros blocos sejam contados até 1000, e cada conjunto de blocos seja um a mais que o último. A mediana do trigésimo percentil descartará todos os 29% dos dados e pouco menos da metade dos 61% dos dados, que são 29 + 30% = 59% dos dados. Opa, acabamos de jogar fora a verdadeira mediana! Então, aparentemente, você não quis dizer isso, ou pelo menos, mais inteligente do que eu interpretei.
Rex Kerr #
0

Vamos primeiro descobrir como encontrar uma mediana de n números em uma única máquina: estou basicamente usando a estratégia de particionamento.

Problema: seleção (n, n / 2): Encontre o n / 2 o número do menor número.

Você escolhe o elemento do meio k e particiona os dados em duas sub-matrizes. o primeiro contém todos os elementos <ke o segundo contém todos os elementos> = k.

se sizeof (1ª sub-matriz)> = n / 2, você sabe que essa sub-matriz contém a mediana. Você pode então retirar o segundo sub-array. Resolva essa seleção de problema (tamanho da 1ª sub-matriz, n / 2) .

Caso contrário, jogue fora esse 1º subarray e resolva a seleção (2º subarray, n / 2 - sizeof (1º subarray))

Faça isso recursivamente.

complexidade do tempo é O (n) tempo esperado.

Agora, se temos muitas máquinas, em cada iteração, temos que processar uma matriz para dividir, distribuímos a matriz em máquinas diff. Cada máquina processa sua parte da matriz e envia de volta o resumo para a máquina controladora de hub, ou seja, tamanho do 1º subarray e tamanho do 2º subarray. As máquinas do hub adicionam resumos e decidem qual subarray (1º ou 2º) processar mais e o segundo parâmetro de seleção e o envia de volta para cada máquina. e assim por diante.

Esse algoritmo pode ser implementado com muito cuidado usando o mapa de redução?

Como se parece?

xyz
fonte
0

Acho que a resposta de Steve Jessop será a mais rápida.

Se o tamanho da transferência de dados da rede for o gargalo, aqui está outra abordagem.

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.
Cem
fonte
32 MB cada, você quer dizer?
Dici
O que você quer dizer com continuar na parte inferior da lista?
Ruthvik Vaila
0

Eu faria assim:

no começo, todos os 100 trabalham para encontrar o número mais alto e o mais baixo; cada computador possui sua parte do banco de dados / arquivo que consulta;

quando os números mais alto e mais baixo são encontrados, um computador lê os dados e distribui cada número igualmente para o restante dos 99; os números são distribuídos em intervalos iguais; (um pode levar de -100 milhões a 0, outro - de 0 a 100 milhões, etc);

Enquanto recebe números, cada um dos 99 computadores já os classifica;

Então, é fácil encontrar a mediana ... Veja quantos números tem cada computador, adicione todos eles (a soma de quantos números existem, não os próprios números), divida por 2; calcular em qual computador é o número e em qual índice;

:) voilla

PS Parece que há muita confusão aqui; A MÉDIA - É O NÚMERO NO MEIO DE UMA LISTA CLASSIFICADA DE NÚMEROS!

Johny
fonte
0

Você pode usar o método da árvore de torneios para encontrar a mediana. Podemos criar uma árvore com 1000 nós de saída, de modo que cada nó folha seja uma matriz. Em seguida, realizamos torneios n / 2 entre as diferentes matrizes. O valor na raiz após os torneios n / 2 é o resultado.

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

karan kapoor
fonte
0

Se os números não são distintos e pertencem apenas a um determinado intervalo, ou seja, eles se repetem, então uma solução simples que me vem à mente é distribuir os números entre 99 máquinas igualmente e manter uma máquina como mestre. Agora, cada máquina itera sobre os números fornecidos e armazena a contagem de cada número em um conjunto de hash. Cada vez que o número é repetido no conjunto de números alocados para esse computador específico, ele atualiza sua contagem no conjunto de hash.

Todas as máquinas retornam seu conjunto de hash para a máquina principal. A máquina principal combina os conjuntos de hash, somando a contagem da mesma chave encontrada em um conjunto de hash. Por exemplo, o conjunto de hash da máquina nº 1 teve uma entrada de ("1", 7) e o conjunto de hash da máquina nº 2 teve uma entrada de ("1", 9); portanto, a máquina principal ao pentear os conjuntos de hash faz uma entrada de ("1", 16) e assim por diante.

Depois que os conjuntos de hash foram mesclados, basta classificar as chaves e agora você pode encontrar facilmente o (n / 2) th item e (n + 2/2) th item, no conjunto de hash classificado.

Este método não será benéfico se os bilhões de números forem distintos.

Eric B.
fonte
0

Bem, suponha que você saiba que o número de números inteiros distintos é (digamos) 4 bilhões, então você pode agrupá-los em intervalos de 64k e obter uma contagem distribuída para cada intervalo de cada máquina no cluster (100 computadores). Combine todas essas contagens. Agora, encontre o depósito com a mediana e, desta vez, solicite apenas os depósitos de 64k elementos que estariam no depósito de destino. Isso requer O (1) (especificamente 2) consultas no seu "cluster". : D

gandharv garg
fonte
0

Meu centavo vale, depois de tudo o que já foi criado por outros:

Encontrar a mediana em uma única máquina é O (N): https://en.wikipedia.org/wiki/Selection_algorithm .

O envio de números N para 100 máquinas também é O (N). Portanto, para tornar interessante o uso de 100 máquinas, a comunicação deve ser relativamente rápida ou N é tão grande que uma única máquina não pode lidar com ela enquanto o N / 100 é possível, ou queremos apenas considerar o problema matemático sem nos preocuparmos com isso. comunicação de dados.

Para resumir, assumirei, portanto, que, dentro de limites razoáveis, podemos enviar / distribuir os números sem afetar a análise de eficiência.

Considere, então, a seguinte abordagem, em que uma máquina é designada para ser o "mestre" para algum processamento geral. Isso será comparativamente rápido, portanto o "mestre" também participa das tarefas comuns que cada máquina executa.

  1. Cada máquina recebe N / 100 dos números, calcula sua própria mediana e envia essas informações ao mestre.
  2. O mestre compila uma lista classificada de todas as medianas distintas e a envia de volta para cada máquina, definindo uma sequência ordenada de buckets (em cada máquina a mesma), uma para cada valor mediano (um bucket de valor único) e uma para cada intervalo entre medianas adjacentes. Obviamente, também existem caçambas de extremidade inferior e superior para valores abaixo da mediana mais baixa e acima da mais alta.
  3. Cada máquina calcula quantos números caem em cada balde e comunica essas informações de volta ao mestre.
  4. O mestre determina qual intervalo contém a mediana, quantos valores mais baixos (no total) ficam abaixo desse intervalo e quantos acima.
  5. Se o depósito selecionado for um depósito de valor único (uma das medianas) ou, em seguida, o depósito selecionado conterá apenas 1 (N ímpar) ou 2 (N pares). Caso contrário, repetimos as etapas acima com as seguintes modificações (óbvias):
  6. Somente os números do balde selecionado são (re) distribuídos do mestre para as 100 máquinas e, além disso,
  7. Não vamos calcular (em cada máquina) a mediana, mas o valor k-ésimo, onde levamos em conta quantos números mais altos foram descartados do total e quantos números mais baixos. Conceitualmente, cada máquina também possui sua parcela dos números baixos / altos descartados e leva isso em consideração ao calcular a nova mediana no conjunto que (conceitualmente) inclui (sua parcela) os números descartados.

Complexidade temporal:

  1. Um pouco de reflexão o convencerá de que, em cada etapa, o número total de valores a serem analisados ​​é reduzido por um fator de pelo menos dois (2 seria um caso bastante doente; você pode esperar uma redução significativamente melhor). A partir disso, obtemos:
  2. Supondo que encontrar a mediana (ou k-ésimo valor), que é O (N), leva c * N tempo em que o prefator c não varia muito com N, para que possamos considerá-la uma constante no momento. obteremos nosso resultado final em no máximo 2 * c * N / 100 vezes. Usar 100 máquinas nos fornece, portanto, um fator de aceleração de 100/2 (pelo menos).
  3. Como observado inicialmente: o tempo envolvido na comunicação dos números entre as máquinas pode tornar mais atraente simplesmente fazer tudo em uma máquina. No entanto, se optarmos pela abordagem distribuída, a contagem total de números a serem comunicados em todas as etapas em conjunto não excederá 2 * N (N pela primeira vez, <= N / 2 na segunda vez, <= metade da terceiro e assim por diante).
Bert te Velde
fonte
-1
  1. Divida os 1 bilhão de números em 100 máquinas. Cada máquina terá 10 ^ 7 números.

  2. Para cada número recebido em uma máquina, armazene o número em um mapa de frequência, número -> contagem. Guarde também o número mínimo em cada máquina.

  3. Encontre mediana em cada máquina: a partir do número mínimo em cada máquina, some as contagens até o índice mediano ser atingido. A mediana em cada máquina será o aprox. menor e maior que 5 * 10 ^ 6 números.

  4. Encontre a mediana de todas as medianas, que será menor e maior que aprox. 50 * 10 ^ 7 números, que é a mediana de 1 bilhão de números.

Agora, alguma otimização da segunda etapa: em vez de armazenar em um mapa de frequência, armazene as contagens em uma matriz de bits variável. Por exemplo: digamos que a partir do número mínimo em uma máquina, estas são as contagens de frequência:

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

O acima pode ser armazenado na matriz de bits como:

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

Observe que, no total, custará cerca de 10 ^ 7 bits para cada máquina, pois cada máquina manipula apenas 10 ^ 7 números. 10 ^ 7bits = 1,25 * 10 ^ 6 bytes, ou seja, 1,25MB

Portanto, com a abordagem acima, cada máquina precisará de 1,25 MB de espaço para calcular a mediana local. E a mediana das medianas pode ser calculada a partir dessas 100 medianas locais, resultando na mediana de 1 bilhão de números.

Shiv
fonte
E se os números forem flutuadores?
Sklivvz
-1

Sugiro um método para calcular aproximadamente a mediana. :) Se esses bilhões de números estiverem em uma ordem aleatória, acho que posso escolher 1/100 ou 1/10 de um bilhão de números aleatoriamente, separá-los com 100 máquinas e depois escolher a mediana deles. Ou vamos dividir bilhões de números em 100 partes, deixar cada máquina escolher 1/10 de cada parte aleatoriamente, calcular a mediana delas. Depois disso, temos 100 números e podemos calcular a mediana do número 100 mais facilmente. Apenas uma sugestão, não tenho certeza se é matematicamente correto. Mas acho que você pode mostrar o resultado para um gerente que não é tão bom em matemática.

rapaz preguiçoso
fonte
É, obviamente, não é correto, e eu recomendo fortemente que você nunca assumir seu entrevistador é um porco estúpido você pode enganar
Dici
Haha ok, embora isso não mude o fato de sua resposta estar incorreta. É muito fácil provar isso
Dici 4/15
OK, depois de ler uma palestra sobre estatística, acho que a ideia de pegar 1/100 ou 1/1000 aleatoriamente de um bilhão de números e calcular sua mediana não é tão ruim. É apenas um cálculo aproximado.
Lazyboy 5/05
-3

A resposta de Steve Jessop está errada:

considere os seguintes quatro grupos:

{2, 4, 6, 8, 10}

{21, 21, 24, 26, 28}

{12, 14, 30, 32, 34}

{16, 18, 36, 38, 40}

A mediana é 21, que está contida no segundo grupo.

A mediana dos quatro grupos é 6, 24, 30, 36; a mediana total é 27.

Então, após o primeiro loop, os quatro grupos se tornarão:

{6, 8, 10}

{24, 26, 28}

{12, 14, 30}

{16, 18, 36}

Os 21 já foram descartados de maneira errada.

Este algoritmo suporta apenas o caso quando existem dois grupos.

senhor das Trevas
fonte