Hace poco asistí a una entrevista en la que me pidieron "escribir un programa para encontrar los 100 números más grandes de un conjunto de mil millones de números".
Solo pude dar una solución de fuerza bruta que consistía en ordenar la matriz en complejidad de tiempo O (nlogn) y tomar los últimos 100 números.
Arrays.sort(array);
El entrevistador estaba buscando una mejor complejidad temporal, probé un par de otras soluciones pero no pude responderle. ¿Existe una mejor solución de complejidad de tiempo?
O(1)
en este caso, porque no hay aumento de dimensión. El entrevistador debería haber preguntado "¿Cómo encontrar m elementos más grandes de una matriz de n con n >> m?".Respuestas:
Puede mantener una cola prioritaria de los 100 números más grandes, recorrer los mil millones de números cada vez que encuentre un número mayor que el número más pequeño en la cola (el encabezado de la cola), elimine el encabezado de la cola y agregue el nuevo número a la cola
EDITAR: como señaló Dev, con una cola prioritaria implementada con un montón, la complejidad de la inserción en la cola es
O(logN)
En el peor de los casos obtienes cuál es mejor que
billionlog2(100)
billion
log2(billion)
En general, si necesita los números K más grandes de un conjunto de números N, la complejidad es
O(NlogK)
más queO(NlogN)
esto, esto puede ser muy significativo cuando K es muy pequeño en comparación con N.EDIT2:
El tiempo esperado de este algoritmo es bastante interesante, ya que en cada iteración puede ocurrir o no una inserción. La probabilidad de que el i-ésimo número se inserte en la cola es la probabilidad de que una variable aleatoria sea mayor que al menos
i-K
variables aleatorias de la misma distribución (los primeros k números se agregan automáticamente a la cola). Podemos usar estadísticas de pedidos (ver enlace ) para calcular esta probabilidad. Por ejemplo, supongamos que los números se seleccionaron aleatoriamente de manera uniforme{0, 1}
, el valor esperado de (iK) th número (de i números) es(i-k)/i
, y la posibilidad de que una variable aleatoria sea mayor que este valor1-[(i-k)/i] = k/i
.Por lo tanto, el número esperado de inserciones es:
Y el tiempo de ejecución esperado se puede expresar como:
(
k
tiempo para generar la cola con los primerosk
elementos, luego lasn-k
comparaciones y el número esperado de inserciones como se describió anteriormente, cada una toma unlog(k)/2
tiempo promedio )Tenga en cuenta que cuando
N
es muy grande en comparación conK
, esta expresión está mucho más cerca que enn
lugar deNlogK
. Esto es algo intuitivo, como en el caso de la pregunta, incluso después de 10000 iteraciones (que es muy pequeño en comparación con mil millones), la posibilidad de que se inserte un número en la cola es muy pequeña.fuente
k
constante y pequeño en comparación conn
. Sin embargo, uno siempre debe tener en cuenta estas "circunstancias normales".Si se pregunta esto en una entrevista, creo que el entrevistador probablemente quiera ver su proceso de resolución de problemas, no solo su conocimiento de algoritmos.
La descripción es bastante general, por lo que quizás pueda preguntarle el rango o el significado de estos números para aclarar el problema. Hacer esto puede impresionar a un entrevistador. Si, por ejemplo, estos números representan la edad de las personas dentro de un país (por ejemplo, China), entonces es un problema mucho más fácil. Con una suposición razonable de que nadie vivo tiene más de 200 años, puede usar una matriz int de tamaño 200 (quizás 201) para contar el número de personas con la misma edad en una sola iteración. Aquí el índice significa la edad. Después de esto, es fácil encontrar el número 100 más grande. Por cierto, este algo se llama orden de conteo .
De todos modos, hacer la pregunta más específica y más clara es bueno para usted en una entrevista.
fuente
Puede iterar sobre los números que toman O (n)
Siempre que encuentre un valor mayor que el mínimo actual, agregue el nuevo valor a una cola circular con tamaño 100.
El mínimo de esa cola circular es su nuevo valor de comparación. Sigue agregando a esa cola. Si está lleno, extraiga el mínimo de la cola.
fuente
Me di cuenta de que esto está etiquetado con 'algoritmo', pero arrojará algunas otras opciones, ya que probablemente también debería etiquetarse 'entrevista'.
¿Cuál es la fuente de los mil millones de números? Si se trata de una base de datos, entonces 'seleccionar el valor del orden de la tabla por el valor desc límite 100' haría el trabajo bastante bien, puede haber diferencias de dialecto.
¿Es esto único o algo que se repetirá? Si se repite, ¿con qué frecuencia? Si es único y los datos están en un archivo, entonces 'cat srcfile | ordenar (opciones según sea necesario) | head -100 'lo hará hacer rápidamente un trabajo productivo por el que le pagan mientras la computadora maneja esta tarea trivial.
Si se repite, aconsejaría elegir un enfoque decente para obtener la respuesta inicial y almacenar / almacenar en caché los resultados para poder informar continuamente sobre los 100 mejores.
Finalmente, hay esta consideración. ¿Está buscando un trabajo de nivel de entrada y se entrevista con un gerente geek o un futuro compañero de trabajo? Si es así, puede descartar todo tipo de enfoques que describan los pros y los contras técnicos relativos. Si está buscando un trabajo más gerencial, acéptelo como lo haría un gerente, preocupado por los costos de desarrollo y mantenimiento de la solución, y diga "muchas gracias" y abandone si ese es el entrevistador que quiere centrarse en la trivia de CS . Es poco probable que él y usted tengan mucho potencial de avance allí.
Mejor suerte en la próxima entrevista.
fuente
Mi reacción inmediata para esto sería usar un montón, pero hay una manera de usar QuickSelect sin tener a mano todos los valores de entrada en cualquier momento.
Cree una matriz de tamaño 200 y llénela con los primeros 200 valores de entrada. Ejecute QuickSelect y descarte los 100 bajos, dejándolo con 100 lugares libres. Lea los siguientes 100 valores de entrada y ejecute QuickSelect nuevamente. Continúe hasta que haya ejecutado toda la entrada en lotes de 100.
Al final tienes los 100 mejores valores. Para los valores de N, ha ejecutado QuickSelect aproximadamente N / 100 veces. Cada selección rápida cuesta aproximadamente 200 veces alguna constante, por lo que el costo total es 2N veces alguna constante. Esto se ve lineal en el tamaño de la entrada para mí, independientemente del tamaño del parámetro que estoy cableado para ser 100 en esta explicación.
fuente
partial_sort
ejecución de libstdc ++ directamente en un conjunto de datos de 200 millones de 32 bitsint
(creado a través de un MT19937, distribuido uniformemente).Ordering.greatestOf(Iterable, int)
. Es absolutamente de tiempo lineal y de un solo paso, y es un algoritmo súper lindo. FWIW, también tenemos algunos puntos de referencia reales: sus factores constantes son mucho más lentos que la cola de prioridad tradicional en el caso promedio, pero esta implementación es mucho más resistente a la entrada del "peor de los casos" (por ejemplo, entrada estrictamente ascendente).Puede usar el algoritmo de selección rápida para encontrar el número en el índice (por orden) [mil millones-101] y luego iterar sobre los números y encontrar los números que aparecen en ese número.
El tiempo de este algoritmo es: 2 XO (N) = O (N) (Rendimiento promedio del caso)
La segunda opción como sugiere Thomas Jungblut es:
Use Heap para construir el montón MAX tomará O (N), luego los 100 números máximos superiores estarán en la parte superior del montón, todo lo que necesita es sacarlos del montón (100 XO (Log (N)).
El tiempo de este algoritmo es: O (N) + 100 XO (Log (N)) = O (N)
fuente
O(N)
, hacer dos QuickSelects y otro escaneo lineal es mucho más sobrecarga de lo necesario.100*O(N)
(si esa es una sintaxis válida) =O(100*N)
=O(N)
(es cierto que 100 puede ser variable, de ser así, esto no es estrictamente cierto). Ah, y Quickselect tiene el peor rendimiento de O (N ^ 2) (ouch). Y si no cabe en la memoria, volverá a cargar los datos del disco dos veces, lo que es mucho peor que una vez (este es el cuello de botella).Aunque la otra solución de selección rápida se ha rechazado, el hecho es que la selección rápida encontrará la solución más rápido que usar una cola de tamaño 100. La selección rápida tiene un tiempo de ejecución esperado de 2n + o (n), en términos de comparaciones. Una implementación muy simple sería
Esto tomará 3n + o (n) comparaciones en promedio. Además, puede hacerse más eficiente utilizando el hecho de que la selección rápida dejará los 100 elementos más grandes de la matriz en las 100 ubicaciones más a la derecha. De hecho, el tiempo de ejecución se puede mejorar a 2n + o (n).
Existe el problema de que este es el tiempo de ejecución esperado, y no el peor de los casos, pero al usar una estrategia de selección pivote decente (p. Ej., Elegir 21 elementos al azar y elegir la mediana de esos 21 como pivote), entonces el número de comparaciones puede ser garantizado con alta probabilidad de ser como máximo (2 + c) n para una constante arbitrariamente pequeña c.
De hecho, mediante el uso de una estrategia de muestreo optimizada (por ejemplo, elementos sqrt (n) de muestra al azar y elegir el percentil 99), el tiempo de ejecución se puede reducir a (1 + c) n + o (n) para c arbitrariamente pequeño (suponiendo que K, el número de elementos a seleccionar es o (n)).
Por otro lado, usar una cola de tamaño 100 requerirá comparaciones O (log (100) n), y la base de registro 2 de 100 es aproximadamente igual a 6.6.
Si pensamos en este problema en el sentido más abstracto de elegir los elementos K más grandes de una matriz de tamaño N, donde K = o (N) pero K y N van al infinito, entonces el tiempo de ejecución de la versión de selección rápida será O (N) y la versión de la cola será O (N log K), por lo que en este sentido la selección rápida también es asintóticamente superior.
En los comentarios, se mencionó que la solución de cola se ejecutará en el tiempo esperado N + K log N en una entrada aleatoria. Por supuesto, el supuesto de entrada aleatoria nunca es válido a menos que la pregunta lo indique explícitamente. Se podría hacer que la solución de la cola atraviese la matriz en un orden aleatorio, pero esto incurrirá en el costo adicional de N llamadas a un generador de números aleatorios, así como también permutando toda la matriz de entrada o asignando una nueva matriz de longitud N que contiene el índices aleatorios
Si el problema no le permite moverse por los elementos en la matriz original, y el costo de asignar memoria es alto, entonces duplicar la matriz no es una opción, eso es algo diferente. Pero estrictamente en términos de tiempo de ejecución, esta es la mejor solución.
fuente
toma los primeros 100 números de los mil millones y clasifícalos. ahora solo repita el billón, si el número de origen es mayor que el menor de 100, inserte en orden de clasificación. Lo que termina es algo mucho más cercano a O (n) sobre el tamaño del conjunto.
fuente
Dos opciones:
(1) Montón (PriorityQueue)
Mantenga un montón mínimo con un tamaño de 100. Atraviese la matriz. Una vez que el elemento es más pequeño que el primer elemento en el montón, reemplácelo.
(2) Modelo de reducción de mapa.
Esto es muy similar al ejemplo de conteo de palabras en hadoop. Trabajo de mapa: cuente la frecuencia o los tiempos de cada elemento aparecidos. Reducir: Obtener el elemento K superior.
Por lo general, le daría al reclutador dos respuestas. Dales lo que quieran. Por supuesto, la codificación de reducción de mapa sería laboriosa, ya que debe conocer todos los parámetros exactos. No hace daño practicarlo. Buena suerte.
fuente
Una solución muy fácil sería recorrer la matriz 100 veces. Lo cual es
O(n)
.Cada vez que extrae el número más grande (y cambia su valor al valor mínimo, para que no lo vea en la próxima iteración, o realice un seguimiento de los índices de respuestas anteriores (al realizar un seguimiento de los índices que puede tener la matriz original) múltiplo del mismo número)). Después de 100 iteraciones, tienes los 100 números más grandes.
fuente
Inspirado por la respuesta de @ron teller, aquí hay un programa básico de C para hacer lo que quieras.
En mi máquina (core i3 con un SSD rápido) tarda 25 segundos y 1724. Generé un archivo binario con
dd if=/dev/urandom/ count=1000000000 bs=1
para esta ejecución.Obviamente, hay problemas de rendimiento con la lectura de solo 4 bytes a la vez, desde el disco, pero esto es por el bien. En el lado positivo, se necesita muy poca memoria.
fuente
La solución más simple es escanear la matriz grande de mil millones de números y mantener los 100 valores más grandes encontrados hasta ahora en un búfer de matriz pequeña sin ningún tipo de clasificación y recordar el valor más pequeño de este búfer. Primero pensé que este método fue propuesto por fordprefect, pero en un comentario dijo que asumió que la estructura de datos de 100 números se implementaba como un montón. Cada vez que se encuentra un nuevo número que es mayor, el mínimo en el búfer se sobrescribe con el nuevo valor encontrado y se busca nuevamente en el búfer el mínimo actual. Si los números en una matriz de mil millones de números se distribuyen aleatoriamente la mayor parte del tiempo, el valor de la matriz grande se compara con el mínimo de la matriz pequeña y se descarta. Solo para una fracción muy pequeña del número, el valor debe insertarse en la matriz pequeña. Por lo tanto, se puede ignorar la diferencia de manipular la estructura de datos que contiene los números pequeños. Para una pequeña cantidad de elementos, es difícil determinar si el uso de una cola prioritaria es realmente más rápido que usar mi enfoque ingenuo.
Quiero estimar el número de inserciones en el pequeño búfer de matriz de 100 elementos cuando se escanea la matriz de 10 ^ 9 elementos. El programa escanea los primeros 1000 elementos de esta gran matriz y tiene que insertar como máximo 1000 elementos en el búfer. El búfer contiene 100 elementos de los 1000 elementos escaneados, es decir, 0.1 del elemento escaneado. Por lo tanto, suponemos que la probabilidad de que un valor de la matriz grande sea mayor que el mínimo actual del búfer es de aproximadamente 0.1 Este elemento debe insertarse en el búfer. Ahora el programa escanea los siguientes 10 ^ 4 elementos de la gran matriz. Debido a que el mínimo del búfer aumentará cada vez que se inserte un nuevo elemento. Estimamos que la proporción de elementos mayores que nuestro mínimo actual es de aproximadamente 0.1 y, por lo tanto, hay 0.1 * 10 ^ 4 = 1000 elementos para insertar. En realidad, el número esperado de elementos que se insertan en el búfer será menor. Después del escaneo de esta fracción de 10 ^ 4 elementos de los números en el búfer será aproximadamente 0.01 de los elementos escaneados hasta ahora. Entonces, al escanear los siguientes 10 ^ 5 números, suponemos que no se insertará más de 0.01 * 10 ^ 5 = 1000 en el búfer. Continuando con esta argumentación, hemos insertado unos 7000 valores después de escanear 1000 + 10 ^ 4 + 10 ^ 5 + ... + 10 ^ 9 ~ 10 ^ 9 elementos de la matriz grande. Entonces, al escanear una matriz con 10 ^ 9 elementos de tamaño aleatorio, esperamos no más de 10 ^ 4 (= 7000 inserciones redondeadas) en el búfer. Después de cada inserción en el búfer, se debe encontrar el nuevo mínimo. Si el búfer es una matriz simple, necesitamos una comparación de 100 para encontrar el nuevo mínimo. Si el búfer es otra estructura de datos (como un montón), necesitamos al menos 1 comparación para encontrar el mínimo. Para comparar los elementos de la gran matriz, necesitamos 10 ^ 9 comparaciones. Así que, en general, necesitamos aproximadamente 10 ^ 9 + 100 * 10 ^ 4 = 1.001 * 10 ^ 9 comparaciones cuando se usa una matriz como buffer y al menos 1.000 * 10 ^ 9 comparaciones cuando se usa otro tipo de estructura de datos (como un montón) . Por lo tanto, usar un montón solo genera una ganancia del 0.1% si el rendimiento está determinado por el número de comparación. Pero, ¿cuál es la diferencia en el tiempo de ejecución entre insertar un elemento en un montón de 100 elementos y reemplazar un elemento en una matriz de 100 elementos y encontrar su nuevo mínimo? 000 * 10 ^ 9 comparaciones cuando se utiliza otro tipo de estructura de datos (como un montón). Por lo tanto, usar un montón solo genera una ganancia del 0.1% si el rendimiento está determinado por el número de comparación. Pero, ¿cuál es la diferencia en el tiempo de ejecución entre insertar un elemento en un montón de 100 elementos y reemplazar un elemento en una matriz de 100 elementos y encontrar su nuevo mínimo? 000 * 10 ^ 9 comparaciones cuando se utiliza otro tipo de estructura de datos (como un montón). Por lo tanto, usar un montón solo genera una ganancia del 0.1% si el rendimiento está determinado por el número de comparación. Pero, ¿cuál es la diferencia en el tiempo de ejecución entre insertar un elemento en un montón de 100 elementos y reemplazar un elemento en una matriz de 100 elementos y encontrar su nuevo mínimo?
En el nivel teórico: cuántas comparaciones son necesarias para insertar en un montón. Sé que es O (log (n)) pero ¿qué tan grande es el factor constante? yo
A nivel de máquina: ¿Cuál es el impacto del almacenamiento en caché y la predicción de ramificación en el tiempo de ejecución de una inserción de montón y una búsqueda lineal en una matriz?
En el nivel de implementación: ¿Qué costos adicionales están ocultos en una estructura de datos de montón provista por una biblioteca o un compilador?
Creo que estas son algunas de las preguntas que deben responderse antes de poder intentar estimar la diferencia real entre el rendimiento de un montón de 100 elementos o una matriz de 100 elementos. Por lo tanto, tendría sentido hacer un experimento y medir el rendimiento real.
fuente
Algoritmo Los elementos x más grandes de n:
Llamaré al valor de retorno LIST . Es un conjunto de elementos x (en mi opinión, debería estar lista enlazada)
Entonces, ¿cuál es el peor de los casos?
x log (x) + (nx) (log (x) +1) = nlog (x) + n - x
Entonces ese es el momento O (n) para el peor de los casos. El +1 es la comprobación de si el número es mayor que el más pequeño en la LISTA. El tiempo esperado para el caso promedio dependerá de la distribución matemática de esos n elementos.
Posibles mejoras.
Este algoritmo puede mejorarse ligeramente para el peor de los casos, pero en mi humilde opinión (no puedo probar esta afirmación), eso degradará el comportamiento promedio. El comportamiento asintótico será el mismo.
La mejora en este algoritmo será que no verificaremos si el elemento es mayor que el más pequeño. Intentaremos insertarlo para cada elemento y, si es más pequeño que el más pequeño, lo ignoraremos. Aunque eso suena absurdo si consideramos solo el peor de los casos, tendremos
x log (x) + (nx) log (x) = nlog (x)
operaciones
Para este caso de uso, no veo más mejoras. Sin embargo, debe preguntarse: ¿qué pasa si tengo que hacer esto más que log (n) veces y para diferentes x-es? Obviamente, ordenaríamos esa matriz en O (n log (n)) y tomaríamos nuestro elemento x siempre que los necesitemos.
fuente
Esta pregunta se respondería con la complejidad N log (100) (en lugar de N log N) con solo una línea de código C ++.
La respuesta final sería un vector donde se garantiza que los primeros 100 elementos serán los 100 números más grandes de su matriz, mientras que los elementos restantes están desordenados
C ++ STL (biblioteca estándar) es bastante útil para este tipo de problemas.
Nota: No estoy diciendo que esta sea la solución óptima, pero habría salvado su entrevista.
fuente
La solución simple sería usar una cola prioritaria, agregar los primeros 100 números a la cola y realizar un seguimiento del número más pequeño en la cola, luego iterar a través de los otros mil millones de números, y cada vez que encontremos uno que sea más grande que el número más grande en la cola de prioridad, eliminamos el número más pequeño, agregamos el nuevo número y nuevamente hacemos un seguimiento del número más pequeño en la cola.
Si los números estuvieran en orden aleatorio, esto funcionaría de maravilla porque a medida que iteramos a través de mil millones de números aleatorios, sería muy raro que el siguiente número esté entre los 100 más grandes hasta ahora. Pero los números pueden no ser aleatorios. Si la matriz ya estaba ordenada en orden ascendente, siempre insertaríamos un elemento en la cola de prioridad.
Por lo tanto, primero elegimos 100,000 números aleatorios de la matriz. Para evitar el acceso aleatorio que podría ser lento, agregamos, por ejemplo, 400 grupos aleatorios de 250 números consecutivos. Con esa selección aleatoria, podemos estar bastante seguros de que muy pocos de los números restantes están entre los primeros cien, por lo que el tiempo de ejecución será muy cercano al de un bucle simple que compara mil millones de números con algún valor máximo.
fuente
Encontrar el top 100 de un billón de números se hace mejor usando un montón mínimo de 100 elementos.
Primero imprima el montón mínimo con los primeros 100 números encontrados. min-heap almacenará el más pequeño de los primeros 100 números en la raíz (arriba).
Ahora, a medida que avanza, el resto de los números solo los compara con la raíz (el más pequeño de los 100).
Si el nuevo número encontrado es mayor que la raíz del montón mínimo, reemplace la raíz con ese número; de lo contrario, ignórelo.
Como parte de la inserción del nuevo número en min-heap, el número más pequeño en el montón llegará a la parte superior (raíz).
Una vez que hayamos pasado por todos los números, tendremos los 100 números más grandes en el montón mínimo.
fuente
He escrito una solución simple en Python en caso de que alguien esté interesado. Utiliza el
bisect
módulo y una lista de retorno temporal que mantiene ordenada. Esto es similar a una implementación de cola prioritaria.Uso con 100,000,000 elementos y entrada en el peor de los casos, que es una lista ordenada:
Tomó alrededor de 40 segundos calcular esto para 100,000,000 elementos, así que tengo miedo de hacerlo por 1 billón. Sin embargo, para ser justos, estaba alimentando la entrada del peor de los casos (irónicamente, una matriz que ya está ordenada).
fuente
Veo muchas discusiones de O (N), así que propongo algo diferente solo para el ejercicio de pensamiento.
¿Existe alguna información conocida sobre la naturaleza de estos números? Si es de naturaleza aleatoria, no vaya más allá y mire las otras respuestas. No obtendrá mejores resultados que ellos.
¡Sin embargo! Vea si cualquier mecanismo de llenado de listas llenó esa lista en un orden particular. ¿Están en un patrón bien definido donde se puede saber con certeza que la mayor magnitud de números se encontrará en una determinada región de la lista o en un determinado intervalo? Puede haber un patrón para ello. Si es así, por ejemplo, si se garantiza que están en algún tipo de distribución normal con la joroba característica en el medio, siempre tienen tendencias ascendentes repetidas entre subconjuntos definidos, tienen un pico prolongado en algún momento T en el medio de los datos establecido como tal vez una incidencia de uso de información privilegiada o una falla del equipo, o tal vez solo tenga un "pico" cada número N como en el análisis de fuerzas después de una catástrofe, puede reducir la cantidad de registros que debe verificar de manera significativa.
De todos modos hay algo para pensar. Tal vez esto le ayude a dar a los futuros entrevistadores una respuesta reflexiva. Sé que me impresionaría si alguien me hiciera esa pregunta en respuesta a un problema como este: me diría que está pensando en la optimización. Solo reconozca que no siempre existe la posibilidad de optimizar.
fuente
Crea una lista vacía de 100 espacios vacíos
Para cada número en la lista de entrada:
Si el número es más pequeño que el primero, omita
De lo contrario, reemplácelo con este número.
Luego, empuje el número a través del intercambio adyacente; hasta que sea más pequeño que el siguiente
Devuelve la lista
Nota: si el
log(input-list.size) + c < 100
, entonces la forma óptima es ordenar la lista de entrada, luego dividir los primeros 100 elementos.fuente
La complejidad es O (N)
Primero cree una matriz de 100 ints; inicialice el primer elemento de esta matriz como el primer elemento de los valores N, realice un seguimiento del índice del elemento actual con otra variable, llámelo CurrentBig
Iterar aunque los valores de N
cuando termine, imprima la matriz M desde CurrentBig 100 veces módulo 100 :-) Para el alumno: asegúrese de que la última línea del código no supere los datos válidos justo antes de que salga el código
fuente
Otro algoritmo O (n):
El algoritmo encuentra los 100 más grandes por eliminación.
considere todos los millones de números en su representación binaria. Comience desde el bit más significativo. Encontrar si el MSB es 1 puede hacerse mediante una operación booleana de multiplicación con un número apropiado. Si hay más de 100 1 en estos millones, elimine los otros números con ceros. Ahora, de los números restantes, proceda con el siguiente bit más significativo. mantenga un recuento del número de números restantes después de la eliminación y continúe siempre que este número sea mayor que 100.
La operación booleana principal puede realizarse paralelamente en GPU
fuente
Descubriría quién tuvo tiempo de poner mil millones de números en una matriz y despedirlo. Debe trabajar para el gobierno. Al menos si tuviera una lista vinculada, podría insertar un número en el medio sin mover medio billón para hacer espacio. Aún mejor, un Btree permite una búsqueda binaria. Cada comparación elimina la mitad de su total. Un algoritmo hash le permitiría llenar la estructura de datos como un tablero de ajedrez, pero no es tan bueno para datos dispersos. Como su mejor opción es tener una matriz de solución de 100 enteros y realizar un seguimiento del número más bajo en su matriz de solución para que pueda reemplazarlo cuando encuentre un número más alto en la matriz original. Tendría que mirar cada elemento en la matriz original suponiendo que no esté ordenado para empezar.
fuente
Puedes hacerlo a
O(n)
tiempo. Simplemente recorra la lista y realice un seguimiento de los 100 números más grandes que haya visto en un punto dado y el valor mínimo en ese grupo. Cuando encuentre un nuevo número más grande, el más pequeño de sus diez, reemplácelo y actualice su nuevo valor mínimo de 100 (puede tomar un tiempo constante de 100 para determinar esto cada vez que lo haga, pero esto no afecta el análisis general )fuente
Administrar una lista separada es un trabajo adicional y debe mover las cosas por toda la lista cada vez que encuentre otro reemplazo. Solo q clasifícalo y toma el top 100.
fuente
Tenga en cuenta especialmente ¡El segundo paso puede ser fácil de calcular en paralelo! Y también será eficiente cuando necesite un millón de elementos más grandes.
fuente
Es una pregunta de Google o de otros gigantes de la industria. Tal vez el siguiente código sea la respuesta correcta esperada por su entrevistador. El costo de tiempo y el costo de espacio dependen del número máximo en la matriz de entrada. Para la entrada de matriz de 32 bits int, el costo de espacio máximo es de 4 * 125M Bytes, el costo de tiempo es de 5 * mil millones.
fuente
hice mi propio código, no estoy seguro de si es lo que está buscando el "entrevistador"
fuente
Posibles mejoras.
Si el archivo contiene 1 billón de números, leerlo podría ser muy largo ...
Para mejorar este funcionamiento puedes:
fuente
Primero tome 1000 elementos y agréguelos en un montón máximo. Ahora saque los primeros 100 elementos máximos y guárdelos en algún lugar. Ahora elija los siguientes 900 elementos del archivo y agréguelos en el montón junto con los últimos 100 elementos más altos.
Siga repitiendo este proceso de recoger 100 elementos del montón y agregar 900 elementos del archivo.
La selección final de 100 elementos nos dará el máximo de 100 elementos de un billón de números.
fuente
Problema: Encuentre m elementos más grandes de n elementos donde n >>> m
La solución más simple, que debería ser obvia para todos, es simplemente hacer m pasos del algoritmo de clasificación de burbujas.
luego imprima los últimos n elementos de la matriz.
Esto no requiere estructuras de datos externas y utiliza un algoritmo que todos conocen.
El tiempo estimado de ejecución es O (m * n). Las mejores respuestas hasta ahora son O (n log (m)), por lo que esta solución no es significativamente más costosa para pequeños m.
No digo que esto no pueda mejorarse, pero esta es, con mucho, la solución más simple.
fuente