Dado dos matrices; $births
que contiene una lista de años de nacimiento que indica cuándo nació alguien y que $deaths
contiene una lista de años de muerte que indica cuándo alguien murió, ¿cómo podemos encontrar el año en que la población era más alta?
Por ejemplo, dados los siguientes arreglos:
$births = [1984, 1981, 1984, 1991, 1996];
$deaths = [1991, 1984];
El año en que la población era más alta debería ser 1996
, porque las 3
personas estaban vivas durante ese año, que fue el conteo de población más alto de todos esos años.
Aquí está el cálculo matemático sobre eso:
El | Nacimiento | Muerte | Población | | ------- | ------- | ------------ | El | 1981 | El | 1 | El | 1984 | El | 2 | El | 1984 | 1984 | 2 | El | 1991 | 1991 | 2 | El | 1996 | El | 3 |
Supuestos
Podemos suponer con seguridad que el año en que nace alguien la población puede aumentar en uno y el año en que alguien murió, la población puede disminuir en uno. Entonces, en este ejemplo, 2 personas nacieron en 1984 y 1 persona murió en 1984, lo que significa que la población aumentó en 1 ese año.
También podemos suponer con seguridad que el número de muertes nunca excederá el número de nacimientos y que no puede ocurrir una muerte cuando la población está en 0.
También podemos suponer con seguridad que los años en ambos $deaths
y $births
nunca serán valores negativos o de coma flotante ( siempre son enteros positivos mayores que 0 ).
Nosotros no podemos asumir que las matrices se ordenarán o que no serán valores duplicados, sin embargo.
Requisitos
Debemos escribir una función para devolver el año en que se produjo la mayor población, dados estos dos arreglos como entrada. La función puede devolver 0
, false
, ""
, o NULL
( cualquier valor Falsey es aceptable ) si las matrices de entrada están vacíos o si la población era siempre a 0 en todas partes. Si la población más alta se produjo en varios años, la función puede regresar el primer año en que se alcanzó la población más alta o cualquier año posterior.
Por ejemplo:
$births = [1997, 1997, 1997, 1998, 1999];
$deaths = [1998, 1999];
/* The highest population was 3 on 1997, 1998 and 1999, either answer is correct */
Además, incluir el Big O de la solución sería útil.
Mi mejor intento de hacer esto sería el siguiente:
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;
}
El algoritmo anterior debería funcionar en tiempo polinómico dado que, en el peor de los O(((n log n) * 2) + k)
casos, n
es el número de elementos que se ordenarán de cada matriz y el k
número de años de nacimiento ( ya que sabemos que k
siempre es asík >= y
) dónde y
es el número de años de muerte. Sin embargo, no estoy seguro de si hay una solución más eficiente.
Mis intereses están puramente en un Big O mejorado de complejidad computacional sobre el algoritmo existente. La complejidad de la memoria no es motivo de preocupación. Tampoco es la optimización del tiempo de ejecución. Al menos no es una preocupación principal . Cualquier optimización de tiempo de ejecución menor / mayor es bienvenida, pero no es el factor clave aquí.
fuente
Respuestas:
Creo que podemos tener
O(n log n)
tiempo conO(1)
espacio adicional al ordenar primero, luego mantener una población actual y un máximo global a medida que iteramos. Traté de usar el año actual como punto de referencia, pero la lógica todavía parecía un poco complicada, así que no estoy seguro de que haya funcionado por completo. Con suerte, puede dar una idea del enfoque.Código JavaScript (contraejemplos / errores bienvenidos)
Si el rango de años
m
, está en el orden den
, podríamos almacenar los conteos para cada año en el rango y tenerO(n)
una complejidad de tiempo. Si quisiéramos ponernos elegantes, también podríamos tenerO(n * log log m)
complejidad en el tiempo, mediante el uso de un trie rápido Y que permita la búsqueda del sucesor aO(log log m)
tiempo.fuente
if(birth_i < death_j){//increment stuff + check max} else{//decrement}; birth_i||=infty; death_j||=infty
. También puedes iterar hastamin(birthSize, deathSize)
. si min es el nacimiento, detente. si min es muerte (sospechoso ...), deténgase y verifique(max + birth.length-i)
Podemos resolver esto en tiempo lineal con la clasificación de cubetas. Digamos que el tamaño de la entrada es n, y el rango de años es m.
El máximo acumulado más grande es su respuesta.
El tiempo de ejecución es O (n + m), y el espacio adicional necesario es O (m).
Esta es una solución lineal en n si m es O (n); es decir, si el rango de años no está creciendo más rápidamente que el número de nacimientos y muertes. Esto es casi cierto para los datos del mundo real.
fuente
O(n): Find the min and max year across births and deaths
2. itera nuevamente a través de todos los nacimientos + muerte:O(n): Parse the births+death array, incrementing the appropriate index of the array
luego haces: O (m): Analiza tu matriz, haciendo un seguimiento de la suma acumulativa y su valor máximo. (no es necesario analizar esta matriz; puede realizar un seguimiento de MAX mientras incrementa los índices en 2)Primero agregue los nacimientos y defunciones en un mapa (
year => population change
), ordénelo por clave y calcule la población corriente sobre eso.Esto debería ser aproximadamente
O(2n + n log n)
, donden
es el número de nacimientos.fuente
ksort($indexed)
) se vuelve irrelevante.$indexed = array_count_values($births);
.Resolví este problema con un requisito de memoria de
O(n+m)
[en el peor de los casos, el mejor de los casosO(n)
]y, complejidad de tiempo de
O(n logn)
.Aquí,
n & m
son la longitud debirths
ydeaths
matrices.No sé PHP o JavaScript. Lo he implementado con Java y la lógica es muy simple. Pero creo que mi idea también se puede implementar en esos idiomas.
Detalles de la técnica:
Usé la
TreeMap
estructura Java para almacenar registros de nacimientos y defunciones.TreeMap
inserta datos ordenados ( basados en clave ) como par (clave, valor), aquí la clave es el año y el valor es la suma acumulativa de nacimientos y defunciones (negativo para defunciones).No necesitamos insertar el valor de muertes que sucedió después del año de nacimiento más alto .
Una vez que TreeMap se llena con los registros de nacimientos y defunciones, todas las sumas acumuladas se actualizan y almacenan la población máxima con el año a medida que avanza.
Entrada y salida de muestra: 1
Entrada y salida de muestra: 2
Aquí, las muertes ocurridas (
1914 & later
) después del último año de nacimiento1913
, no se contaron en absoluto, lo que evita cálculos innecesarios.Para un total de
10 million
datos (nacimientos y muertes combinados) y más1000 years range
, el programa tardó3 sec.
en terminar.Si datos del mismo tamaño con
100 years range
, tomó1.3 sec
.Todas las entradas se toman al azar.
fuente
Esto explicará la posibilidad de un año atado, así como si un año de la muerte de alguien no corresponde al nacimiento de alguien.
fuente
En cuanto a la memoria es mantener
currentPopulation
ycurrentYear
calcular. Comenzar clasificando ambas$births
y las$deaths
matrices es un muy buen punto, porque la clasificación de burbujas no es una tarea tan pesada, pero permite cortar algunas esquinas:no estoy realmente interesado en sumergirse en Big O , te lo dejé a ti.
Además, si redescubre
currentYearComputing
todos los bucles, puede cambiar los bucles enif
declaraciones y salir con un solo bucle.fuente
Lleno muy cómodo de esta solución, la complejidad Big O es n + m
fuente
$tmpArray--
ser$tmpArray[$death]--
? También pruebe con$births=[1997,1997,1998]; $deaths=[];
: ¿regresa1998
como debería?$births = [3,1,2,1,3,3,2]
y$deaths = [2,3,2,3,3,3]
esperaría volver2
como el año de mayor población, pero su código regresa1
. De hecho, su código falló 9 de 15 de mis pruebas unitarias . No solo no puedo aceptar esto como la respuesta más eficiente, sino que ni siquiera puedo aceptarlo como una respuesta eficiente ya que no funciona en absoluto.Uno de los enfoques más simples y claros para su problema.
salida :
complejidad :
fuente
29.64 milliseconds