Digamos que você tenha um avião, e ele está com pouco combustível. A menos que o avião caia 3000 libras de peso do passageiro, ele não poderá chegar ao próximo aeroporto. Para salvar o número máximo de vidas, gostaríamos de tirar as pessoas mais pesadas do avião primeiro.
E sim, existem milhões de pessoas no avião, e gostaríamos de um algoritmo ideal para encontrar os passageiros mais pesados, sem necessariamente classificar a lista inteira.
Este é um problema de proxy para algo que estou tentando codificar em C ++. Gostaria de fazer uma "parcial_sort" no manifesto do passageiro por peso, mas não sei quantos elementos vou precisar. Eu poderia implementar meu próprio algoritmo "parcial_sort" ("parcial_sort_accumulate_until"), mas estou pensando se há alguma maneira mais fácil de fazer isso usando o STL padrão.
Respostas:
Uma maneira seria usar um heap mínimo (
std::priority_queue
em C ++). Aqui está como você faria isso, assumindo que você teve umaMinHeap
aula. (Sim, meu exemplo está em C #. Acho que você entendeu.)De acordo com as referências padrão, o tempo de execução deve ser proporcional a
n log k
, onden
é o número de passageiros ek
o número máximo de itens na pilha. Se assumirmos que o peso dos passageiros normalmente será de 100 libras ou mais, é improvável que o heap contenha mais de 30 itens a qualquer momento.O pior caso seria se os passageiros fossem apresentados na ordem do menor para o maior. Isso exigiria que todos os passageiros fossem adicionados à pilha e todos os passageiros fossem removidos da pilha. Ainda assim, com um milhão de passageiros e assumindo que o mais leve pesa 100 libras, o
n log k
resultado é um número razoavelmente pequeno.Se você obtiver os pesos dos passageiros aleatoriamente, o desempenho será muito melhor. Uso algo assim para um mecanismo de recomendação (seleciono os 200 itens principais de uma lista de vários milhões). Normalmente, acabo com apenas 50.000 ou 70.000 itens realmente adicionados ao heap.
Eu suspeito que você verá algo bem parecido: a maioria dos seus candidatos será rejeitada porque é mais leve que a pessoa mais leve que já está na pilha. E
Peek
é umaO(1)
operação.Para obter mais informações sobre o desempenho da seleção de pilha e seleção rápida, consulte Quando a teoria atender à prática . Versão curta: se você estiver selecionando menos de 1% do número total de itens, a seleção de heap é uma clara ganhadora da seleção rápida. Mais de 1%, use a seleção rápida ou uma variante como o Introselect .
fonte
std::priority_queue
Isso não ajudará no seu problema de proxy, no entanto:
Para que 1.000.000 de passageiros deixem cair 3000 libras de peso, cada passageiro deve perder (3000/1000000) = 0,003 libras por pessoa. Isso poderia ser alcançado através do descarte de todas as camisas, sapatos ou, provavelmente, até recortes de unhas, salvando a todos. Isso pressupõe coleta e descarte eficientes antes que a perda de peso necessária aumente conforme o avião consome mais combustível.
Na verdade, eles não permitem mais cortadores de unhas a bordo, então está fora.
fonte
Abaixo está uma implementação bastante simples da solução direta. Não acho que exista uma maneira mais rápida e 100% correta.
Isso funciona preenchendo o conjunto de "pessoas mortas" até atingir o limite. Uma vez atingido o limite, continuamos analisando a lista de passageiros que tentam encontrar outros que sejam mais pesados que a pessoa morta mais leve. Quando encontramos um, os adicionamos à lista e começamos a "Salvar" as pessoas mais leves da lista até que não possamos mais salvar.
Na pior das hipóteses, isso terá o mesmo desempenho que uma espécie de lista inteira. Mas, na melhor das hipóteses (a "lista de mortos" é preenchida corretamente com as primeiras pessoas X), ela será executada
O(n)
.fonte
total
lado decontinue;
Other than that, esta é a resposta que eu ia postar. Solução super rápidaSupondo que todos os passageiros cooperem: Use uma rede de classificação paralela . (veja também isso )
Aqui está uma demonstração ao vivoAtualização: vídeo alternativo (pule para 1:00)
Pedindo a pares para troca de comparação - você não pode ficar mais rápido que isso.
fonte
n
processadores não se sustenta.@Blastfurnace estava no caminho certo. Você usa a seleção rápida onde os pivôs são limites de peso. Cada partição divide um conjunto de pessoas em conjuntos e retorna o peso total de cada conjunto de pessoas. Você continua quebrando o balde apropriado até que seus baldes correspondentes às pessoas com maior peso tenham mais de 3000 libras e seu balde mais baixo nesse conjunto tenha 1 pessoa (ou seja, não poderá mais ser dividido).
Esse algoritmo é amortizado no tempo linear, mas no pior dos casos quadrático. Eu acho que é o único algoritmo de tempo linear .
Aqui está uma solução Python que ilustra esse algoritmo:
Resultado:
fonte
Supondo que, como o peso das pessoas, você tenha uma boa idéia de quais valores máximos e mínimos provavelmente usarão uma classificação radix para classificá-los em O (n). Em seguida, simplesmente trabalhe do final mais pesado da lista para o mais leve. Tempo total de execução: O (n). Infelizmente, não há uma implementação de uma classificação radix no STL, mas é bastante simples de escrever.
fonte
Por que você não usa um quicksort parcial com uma regra de cancelamento diferente de "classificado". Você pode executá-lo e, em seguida, usar apenas a metade superior e continuar até que o peso nessa metade superior não contenha o peso que precisa ser jogado fora, pelo menos, você retrocede um passo na recursão e classifica a lista. Depois disso, você pode começar a expulsar pessoas da extremidade alta dessa lista classificada.
fonte
Massively Parallel Tournament Sort: -
Assumindo três assentos padrão de cada lado da ailse: -
Peça aos passageiros no assento da janela que se movam para o assento do meio, se forem mais pesados que a pessoa no assento da janela.
Peça aos passageiros no assento do meio que troquem com o passageiro no assento do corredor, se eles forem mais pesados.
Peça ao passageiro no banco do corredor esquerdo que troque com o passageiro no banco do corredor direito, se eles forem mais pesados.
Bolha classifique os passageiros no assento do corredor direito. (Executa n etapas para n linhas). - peça aos passageiros no assento do corredor direito que troquem com a pessoa na frente n -1 vezes.
5 Chute-os para fora da porta até atingir 3000 libras.
3 degraus + n degraus mais 30 degraus, se você tiver uma carga de passageiros muito fina.
Para um plano de dois corredores - as instruções são mais complexas, mas o desempenho é praticamente o mesmo.
fonte
Eu provavelmente usaria
std::nth_element
para dividir as 20 pessoas mais pesadas em tempo linear. Em seguida, use um método mais complexo para encontrar e esbarrar no mais pesado dos pesados.fonte
Você pode fazer uma passagem na lista para obter a média e o desvio padrão e usá-lo para aproximar o número de pessoas que precisam ir. Use o parcial_sort para gerar a lista com base nesse número. Se o palpite estiver baixo, use o parcial_sort novamente no restante com um novo palpite.
fonte
O @ James tem a resposta nos comentários: a
std::priority_queue
se você puder usar qualquer contêiner ou uma combinação destd::make_heap
estd::pop_heap
(estd::push_heap
) se quiser usar algo como astd::vector
.fonte
Aqui está uma solução baseada em heap usando o módulo heapq interno do Python. Está em Python, portanto, não responde à pergunta original, mas é mais limpo (IMHO) do que a outra solução Python publicada.
Se k = o número de passageiros a lançar e N = o número de passageiros, o melhor caso para esse algoritmo é O (N) e o pior caso para esse algoritmo é Nlog (N). O pior caso ocorre se k estiver próximo de N por um longo período de tempo. Aqui está um exemplo do pior elenco:
No entanto, nesse caso (jogando pessoas fora do avião (com pára-quedas, presumo)), então k deve ser menor que 3000, ou seja, "milhões de pessoas". O tempo médio de execução deve, portanto, ser sobre o Nlog (k), que é linear ao número de pessoas.
fonte