Dada duas matrizes; $births
contendo uma lista de anos de nascimento indicando quando alguém nasceu e $deaths
uma lista de anos de morte indicando quando alguém morreu, como podemos encontrar o ano em que a população foi mais alta?
Por exemplo, dadas as seguintes matrizes:
$births = [1984, 1981, 1984, 1991, 1996];
$deaths = [1991, 1984];
O ano em que a população era mais alta deveria ser 1996
, porque as 3
pessoas estavam vivas durante esse ano, que era a maior contagem de população de todos esses anos.
Aqui está a matemática em execução nisso:
| Nascimento Morte População | | ------- | ------- | ------------ | | 1981 | 1 | | 1984 | 2 | 1984 1984 2 | 1991 1991 2 | 1996 | 3
Premissas
Podemos assumir com segurança que o ano em que alguém nasce, a população pode aumentar em um e o ano em que alguém morreu, a população pode diminuir em um. Portanto, neste exemplo, 2 pessoas nasceram em 1984 e 1 pessoa morreu em 1984, significando que a população aumentou 1 naquele ano.
Também podemos assumir com segurança que o número de mortes nunca excederá o número de nascimentos e que nenhuma morte poderá ocorrer quando a população estiver em 0.
Também podemos assumir com segurança que os anos em ambos $deaths
e $births
nunca serão valores negativos ou de ponto flutuante ( eles sempre são números inteiros positivos maiores que 0 ).
Não podemos assumir que as matrizes serão classificadas ou que não haverá valores duplicados.
Exigências
Devemos escrever uma função para retornar o ano em que a população mais alta ocorreu, considerando essas duas matrizes como entrada. A função pode retornar 0
, false
, ""
, ou NULL
( qualquer valor Falsey é aceitável ) se os arrays de entrada estão vazias ou se a população foi sempre a 0 durante todo. Se a população mais alta ocorreu em vários anos, a função pode retornar o primeiro ano em que a população mais alta foi atingida ou em qualquer ano subsequente.
Por exemplo:
$births = [1997, 1997, 1997, 1998, 1999];
$deaths = [1998, 1999];
/* The highest population was 3 on 1997, 1998 and 1999, either answer is correct */
Além disso, incluir o Big O da solução seria útil.
Minha melhor tentativa de fazer isso seria a seguinte:
function highestPopulationYear(Array $births, Array $deaths): Int {
sort($births);
sort($deaths);
$nextBirthYear = reset($births);
$nextDeathYear = reset($deaths);
$years = [];
if ($nextBirthYear) {
$years[] = $nextBirthYear;
}
if ($nextDeathYear) {
$years[] = $nextDeathYear;
}
if ($years) {
$currentYear = max(0, ...$years);
} else {
$currentYear = 0;
}
$maxYear = $maxPopulation = $currentPopulation = 0;
while(current($births) !== false || current($deaths) !== false || $years) {
while($currentYear === $nextBirthYear) {
$currentPopulation++;
$nextBirthYear = next($births);
}
while($currentYear === $nextDeathYear) {
$currentPopulation--;
$nextDeathYear = next($deaths);
}
if ($currentPopulation >= $maxPopulation) {
$maxPopulation = $currentPopulation;
$maxYear = $currentYear;
}
$years = [];
if ($nextBirthYear) {
$years[] = $nextBirthYear;
}
if ($nextDeathYear) {
$years[] = $nextDeathYear;
}
if ($years) {
$currentYear = min($years);
} else {
$currentYear = 0;
}
}
return $maxYear;
}
O algoritmo acima deve funcionar em tempo polinomial, dado que, na pior das hipóteses, O(((n log n) * 2) + k)
onde n
está o número de elementos a serem classificados de cada matriz e o k
número de anos de nascimento ( já que sabemos que k
é semprek >= y
) onde y
está o número de anos de morte. No entanto, não tenho certeza se existe uma solução mais eficiente.
Meus interesses são puramente em um Big O aprimorado de complexidade computacional sobre o algoritmo existente. A complexidade da memória não é preocupante. Nem é a otimização de tempo de execução. Pelo menos não é uma preocupação principal . Quaisquer otimizações de tempo de execução menores / maiores são bem-vindas, mas não são o fator chave aqui.
fonte
Respostas:
Acho que podemos ter
O(n log n)
tempo comO(1)
espaço adicional classificando primeiro e depois mantendo a população atual e o máximo global à medida que iteramos. Tentei usar o ano atual como ponto de referência, mas a lógica ainda parecia um pouco complicada, por isso não tenho certeza se está completamente elaborado. Felizmente, ele pode dar uma idéia da abordagem.Código JavaScript (contra-exemplos / bugs são bem-vindos)
Se o intervalo do ano,,
m
estiver na ordem den
, poderíamos armazenar as contagens para cada ano no intervalo e terO(n)
complexidade de tempo. Se quiséssemos ser extravagantes, também poderíamos terO(n * log log m)
complexidade de tempo, usando um teste Y-fast que permite a pesquisa sucessora aO(log log m)
tempo.fonte
if(birth_i < death_j){//increment stuff + check max} else{//decrement}; birth_i||=infty; death_j||=infty
. Além disso, você pode iterar atémin(birthSize, deathSize)
. se min é nascimento, pare. se min é morte (suspeito ..), pare e verifique(max + birth.length-i)
Podemos resolver isso em tempo linear com a classificação de bucket. Digamos que o tamanho da entrada seja n e o intervalo de anos seja m.
O maior máximo cumulativo é a sua resposta.
O tempo de execução é O (n + m) e o espaço adicional necessário é O (m).
Esta é uma solução linear em n se m for O (n); ou seja, se o intervalo de anos não estiver crescendo mais rapidamente do que o número de nascimentos e mortes. Isso é quase certamente verdade para dados do mundo real.
fonte
O(n): Find the min and max year across births and deaths
2. itere novamente por todos os nascimentos + morte:O(n): Parse the births+death array, incrementing the appropriate index of the array
e faça: O (m): Analise sua matriz, mantendo o controle da soma acumulada e seu valor máximo. (você não precisa analisar essa matriz - pode acompanhar o MAX enquanto aumenta os índices em 2)Primeiro agregue os nascimentos e mortes em um mapa (
year => population change
), classifique-o por chave e calcule a população em execução sobre ele.Isso deve ser aproximadamente
O(2n + n log n)
, onden
é o número de nascimentos.fonte
ksort($indexed)
) torna-se irrelevante.$indexed = array_count_values($births);
.Resolvi esse problema com um requisito de memória de
O(n+m)
[no pior dos casos, no melhor casoO(n)
]e, complexidade do tempo de
O(n logn)
.Aqui,
n & m
estão o comprimentobirths
e asdeaths
matrizes.Eu não sei PHP ou javascript. Eu o implementei com Java e a lógica é muito simples. Mas acredito que minha ideia também possa ser implementada nesses idiomas.
Detalhes da técnica:
Usei a
TreeMap
estrutura java para armazenar registros de nascimentos e óbitos.TreeMap
insere dados classificados (com base em chave ) como par (chave, valor), aqui a chave é o ano e o valor é a soma cumulativa de nascimentos e mortes (negativa para mortes).Não precisamos inserir o valor das mortes que ocorreram após o ano de nascimento mais alto .
Depois que o TreeMap é preenchido com os registros de nascimentos e óbitos, todas as somas acumuladas são atualizadas e armazenam a população máxima com o ano à medida que avançava.
Entrada e saída de amostra: 1
Entrada e saída de amostra: 2
Aqui, as mortes ocorreram (
1914 & later
) após o último ano de nascimento1913
, não foram contabilizadas, o que evita cálculos desnecessários.Para um total de
10 million
dados (nascimentos e mortes combinados) e mais1000 years range
, o programa demorou3 sec.
para terminar.Se os dados do mesmo tamanho com
100 years range
, levou1.3 sec
.Todas as entradas são obtidas aleatoriamente.
fonte
Isso explicará a possibilidade de um ano vinculado, bem como se um ano da morte de alguém não corresponder ao nascimento de alguém.
fonte
Memória é para manter
currentPopulation
ecurrentYear
calculado. Começar por classificar as duas$births
e as$deaths
matrizes é um ponto muito bom, porque a classificação de bolhas não é uma tarefa pesada, mas permite cortar alguns cantos:realmente interessado em mergulhar na coisa do Big O , deixou isso para você.
Além disso, se você redescobrir
currentYearComputing
cada loop, poderá alterar os loops emif
instruções e sair com apenas um loop.fonte
Encho muito confortável essa solução, a complexidade Big O é n + m
fonte
$tmpArray--
ser$tmpArray[$death]--
? Teste também com$births=[1997,1997,1998]; $deaths=[];
- Ele retorna1998
como deveria?$births = [3,1,2,1,3,3,2]
e$deaths = [2,3,2,3,3,3]
eu esperaria voltar2
como o ano com maior população, mas seu código retornará1
. Na verdade, seu código falhou em 9 de 15 dos meus testes de unidade . Não só não posso aceitar isso como a resposta mais eficiente, mas também não posso aceitá-la como uma resposta eficiente, pois não funciona de todo.Uma das abordagens mais simples e claras para o seu problema.
saída :
complexidade :
fonte
29.64 milliseconds