Se le proporciona un archivo que contiene todos los números posibles en una arquitectura de 32 bits. Faltan 4 números de ese archivo. Encuentra los 4 números que faltan

22

Esta es una pregunta de entrevista que me he encontrado varias veces, y realmente no estoy seguro de cómo resolverla dado que faltan cuatro números. Estoy familiarizado con los algoritmos para encontrar uno o dos números que faltan, pero no veo una forma de generalizar ninguno de ellos a cuatro.

Tsutarja47
fuente

Respuestas:

19

Ya sea para una entrevista o un trabajo real, su primera prioridad debe ser una solución de trabajo que tenga sentido para usted . Que por lo general significa que debe ofrecer la primera solución que se pueda imaginar que es simple y fácil para usted para explicarle.

Para mí, eso significa ordenar los números y buscar huecos. Pero trabajo en sistemas comerciales y aplicaciones web. ¡No jugueteo con pedacitos, y no quiero que mi equipo lo haga!

Si está entrevistando para un trabajo de bajo nivel, más cercano al metal, la "clasificación" probablemente se encontrará con miradas en blanco. Quieren que te sientas cómodo pensando en cosas y demás. Su primera respuesta debería ser: "Oh, usaría un mapa de bits". (O conjunto de bits o conjunto de bits).

Y luego, de cualquier manera, incluso si da una solución "incorrecta", si su entrevistador (¡o su jefe!) Lo presiona , puede sugerir algunas mejoras o alternativas, centrándose en el área específica de interés del gerente.

  • RAM severamente limitada? ¿Menos de 512 MB?
    Clasifíquelo en su lugar, en el disco. Puede utilizar una cantidad de RAM mayoritariamente arbitraria para optimizar y / o ordenar los bloques en el búfer.
  • ¿Tiempo limitado?
    ¡Usa esa RAM! La clasificación ya está O(n*log(n)). (O O (n) para un tipo de cubo entero!)
  • Mantenibilidad?
    ¿Qué podría ser más fácil que ordenar?
  • ¿No demuestra conocimiento de banderas / campos de bits? ( BitSet/ BitMap/ BitArray)
    Bueno, está bien ... adelante y usa a BitArraypara marcar los "números encontrados" Y luego escanea por 0's.
  • Complejidad predecible "en tiempo real"?
    Use la solución de mapa de bits. Es un solo paso sobre el archivo y otro paso sobreBitArray/BitSet(para encontrar el0's). Eso esO(n), creo!

O lo que sea.

Aborde las preocupaciones que realmente tiene. Simplemente resuelva el problema primero, utilizando soluciones ingenuas si es necesario. No pierdas el tiempo de todos abordando preocupaciones que aún no existen.

svidgen
fuente
No estoy tan seguro sobre la viabilidad de clasificar 4 mil millones de números con un enfoque ingenuo, y mucho menos en el disco. Sin embargo, nunca lo he intentado.
Eiko
1
@Eiko Bueno ... y de nuevo, el punto principal es ... no complicar demasiado las cosas. El primer paso es simplemente resolver el problema, de cualquier manera que se te ocurra, incluso si es ingenuo. Ni siquiera puedo enfatizar el nivel de frustración que tendrá su futuro empleador si pasa tiempo iterando para asegurarse de tener la solución "correcta" cuando la empresa solo necesita una solución. ¡Demuestra que puedes hacer ambas cosas! Demuestre que puede resolver problemas rápidamente y luego identifique problemas potenciales que valga la pena refactorizar y / o optimizar según sea necesario .
svidgen
1
@Ewan "Debido a que se te ha planteado la pregunta en la entrevista" no es lo mismo que "hay una respuesta específica que todo gerente está buscando". ... Ciertamente no me importaría la solución que me diste, ¡siempre y cuando demuestres la capacidad de resolver el problema y no quedarte atrapado resolviendo problemas que nunca te di!
svidgen
1
Te estás perdiendo el punto. Esta pregunta y sus variaciones ocurren en libros de rompecabezas de programación y preguntas de entrevistas. No ha sido inventado por la persona que hace la pregunta. Se supone que las cosas de 32 bits hacen que sea imposible hacer un seguimiento de los números o la clasificación. Sus computadoras se han vuelto más rápidas / más grandes desde que se escribió.
Ewan
1
@Ewan: todavía estás asumiendo que tu instancia de la pregunta tiene las mismas restricciones que los OP. El OP no dijo que su algoritmo debe ejecutarse en una máquina de 32 bits, ni siquiera dijo que tiene que ejecutarse en una computadora, un algoritmo conceptual podría ser adecuado. Tampoco dice qué significa "todos los números posibles", ya que las matemáticas enteras de tamaño arbitrario son posibles incluso en microcontroladores de 8 bits. Muchas suposiciones que estás haciendo para dar declaraciones absolutas.
whatsisname
19

Como se trata de un archivo, supongo que puede hacer varios pases. Primero cree una matriz de 256 contadores, itere sobre el archivo y para cada número incremente el contador indexado como el primer byte del número. Cuando haya terminado, la mayoría de los contadores deben estar en 2 ^ 24, pero 1 hasta 4 contadores deben tener valores más bajos. Cada uno de estos índices representa un primer byte de uno de los números faltantes (si hay menos de 4 es porque varios números faltantes comparten el mismo primer byte).

Para cada uno de estos índices, cree otra matriz de 256 contadores y realice una segunda pasada en el archivo. Esta vez, si el primer byte es uno de los valores anteriores, incremente un contador en su matriz en función del segundo byte. Cuando haya terminado, busque nuevamente los contadores inferiores a 2 ^ 16, y obtendrá el segundo byte de los números que faltan, cada uno de los cuales coincide con su primer byte.

Vuelva a hacerlo para el tercer byte (tenga en cuenta que necesita un máximo de 4 matrices en cada pasada, aunque cada byte puede ir seguido de hasta 4 bytes diferentes) y para el cuarto byte, y ha encontrado todos los números que faltan.

Complejidad del tiempo - Complejidad del O(n * log n)
espacio - ¡ constante !

Editar:

En realidad, consideré n=2^32que era el parámetro, pero el número de números faltantes k=4también es un parámetro. Suponiendo que k<<nesto significa que la complejidad del espacio es O(k).

Actualizar:

Solo por diversión (y porque actualmente estoy tratando de aprender Rust) lo implementé en Rust: https://gist.github.com/idanarye/90a925ebb2ea57de18f03f570f70ea1f . Elegí tener una representación textual, ya que uno la ejecutará con ~ 2 ^ 32 números ...

Idan Arye
fuente
Mantener todos los números en la memoria (para pases múltiples) requiere 4 bytes * 2 ^ 32 de memoria, lo que está empujando las cosas. Por lo tanto, es más probable que haga todas las E / S cuatro veces. Pero la otra memoria utilizada es extremadamente pequeña, por lo que es un gran trabajo allí.
user949300
1
@ user949300 Supongo que esta solución lee el archivo pieza por pieza en lugar de cargar todo en la memoria de una vez
Richard Tingle,
"la mayoría de los contadores deben estar en 2 ^ 24, pero 1 hasta 4 contadores deben tener valores más bajos" - incorrecto: puede ser 0, con todos los valores faltantes compartiendo el primer byte (también es posible el segundo y el tercero). A continuación: ¿cuántos arreglos creas en el segundo paso? 256, 1 a 4 veces 256, 256 veces 256? ¿Y luego en el tercer y cuarto pase?
Bernhard Hiller
3
@BernhardHiller El archivo contiene todos los números posibles en el espacio de 32 bits, excepto 4 números distintos. Como tal, todos los primeros bytes ocurrirán, solo 1 a 4 de ellos tendrán menos visitas.
Lasse V. Karlsen
@ LasseV.Karlsen gracias, ahora entiendo el algoritmo.
Bernhard Hiller
6

Si esto fuera Java, podría usar un BitSet. Bueno, dos de ellos, porque no pueden contener todos los números de 32 bits. Código esquelético, quizás con errores:

BitSet bitsetForPositives = new Bitset(2^31);  // obviously not 2^31 but you get the idea
BitSet bitsetForNegatives = new Bitset(2^31);

for (int value: valuesTheyPassInSomehow) {
  if ((value & 0x80000000) == 0)
     bitsetForPositives.set(value );
  else
     bitsetForNegatives.set(value & ~0x80000000);
}

Luego use BitSet.nextClearBit()para encontrar quién falta.

Nota añadida mucho más tarde:

Tenga en cuenta que con este algoritmo, es bastante fácil ejecutar la parte que consume mucho tiempo en paralelo . Digamos que el archivo original se ha dividido en cuatro partes más o menos iguales. Asigne 4 pares de BitSets (2GB, aún manejables).

  1. Tenga cuatro subprocesos, en paralelo, cada uno procesa un archivo en su propio par de BitSets.
  2. Cuando termine, regrese a un solo subproceso, o Bitsets (tiempo trivial), luego llame a nextClearBit cuatro veces (también tiempo bastante trivial).

Esperaría que la E / S siga siendo el paso de limitación de velocidad, pero si mágicamente todos los números estuvieran en la memoria, realmente podría acelerar las cosas.

user949300
fuente
3
@Idan Ayre. Esta solución requiere poco código, por lo que hay menos posibilidades de errores de codificación. Soy bonita, este es el momento O (n). Tampoco supone / requiere pases múltiples a través de un archivo enorme, por lo que utiliza menos espacio que un algoritmo que requiere pases múltiples. Por favor, explique qué quiere decir con "Oh querido".
user949300
2
No se maneja Integer.MIN_VALUEcorrectamente. Podrías enmascarar el bit de signo en lugar de negar arreglarlo.
CodesInChaos
1
Este enfoque ingenuo necesita 2 ^ 32 bits = 4 Gib = 512 MiB para los conjuntos de bits, que es una cantidad modesta de RAM, incluso en un sistema de 32 bits.
CodesInChaos
Si el idioma de elección no tiene conjuntos de bits integrados, emúlelos utilizando una matriz de bytes. Por ejemplo en C #:bool GetBit(byte[] byteArray, uint index) { var byteIndex = index >> 3; var bitInByte = index & 7; return (byteArray[byteIndex] >> bitInByte) & 1 != 0; }
CodesInChaos
1
@JoulinRouge (y JacquesB) Entonces, estamos de acuerdo en que esto es lineal en el tiempo, usa RAM modesta (1/2 Gig), y solo toma una pasada de E / S. Funciona para mi.
user949300
5

Esta pregunta se puede resolver utilizando una matriz de bits (verdadero / falso). Esta debería ser la estructura más eficiente para contener las respuestas para todos los números usando el índice de la matriz para contener si se encontró ese número en particular.

DO#

var bArray = new BitArray(Int32.MaxValue);

//Assume the file has 1 number per line
using (StreamReader sr = File.OpenText(fileName))
{
        string s = String.Empty;
        while ((s = sr.ReadLine()) != null)
        {
            var n = int32.Parse(s);
            bArray[n] = true;
        }
}

Luego, simplemente repita la matriz y para aquellos valores que aún son falsos, no están en el archivo.

Podría dividir el archivo en fragmentos más pequeños, pero pude asignar una matriz de tamaño máximo int32 (2147483647) en mi computadora portátil de 16.0 GB con Windows 7 (64 bits).

Incluso si no estuviera ejecutando 64 bits, podría asignar matrices de bits más pequeñas. Preprocesaría el archivo creando un conjunto de archivos más pequeños, cada uno con un rango de [0-64000] [64001-128000], etc., números que serían adecuados para los recursos ambientales disponibles. Revise el archivo grande y escriba cada número en su archivo de conjunto correspondiente. Luego procese cada archivo más pequeño. Llevaría un poco más de tiempo debido al paso de preprocesamiento, pero esto evitaría las limitaciones de recursos si hubiera recursos limitados.

Jon Raynor
fuente
Esto no parece manejar números negativos. (O entradas sin firmar con el bit más alto establecido si esa es la entrada). La memoria para el conjunto de bits no debería ser un problema, incluso en la mayoría de los sistemas de 32 bits.
user949300
@ user949300 - Correcto. No noté ningún gran consumo de memoria cuando la matriz se inicializó con todos los valores falsos. Se necesitaría un BitArray secundario para los números negativos. Quizás bArrayNegative = new BitArrary (Int32.MaxValue). Cuando se leía el número, se podía verificar si era positivo o negativo y luego se colocaba en la matriz de bits adecuada. Gracias por los comentarios.
Jon Raynor
2

Como se trata de una pregunta de entrevista, le mostraría al entrevistador cierta comprensión sobre las limitaciones. Entonces, ¿qué significa "todos los números posibles"? ¿Es realmente 0 ... 2 <(32-1) como todos adivinan? Las arquitecturas habituales de 32 bits pueden funcionar con muchos más que solo números de 32 bits. Es solo una cuestión de representación, obviamente.

¿Tiene que resolverse en un sistema de 32 bits, o es más bien una parte de la restricción de números? Por ejemplo, un sistema típico de 32 bits no podrá cargar el archivo en la RAM de una vez. También mencionaría que un sistema de 32 bits a menudo no podrá tener un archivo que contenga todos los números debido a la limitación del tamaño del archivo. Bueno, a menos que tenga una codificación inteligente, como "Todos los números excepto esos cuatro", en cuyo caso el problema se resuelve trivialmente.

Pero si realmente quieres entender la pregunta como "Dado un archivo con todos los números del 0 ... 2 ^ (32-1) excepto algunos, dame los que faltan" (¡y este es un gran si !), Entonces Hay muchas formas de resolverlo.

Trivial pero no factible: para cada número posible, escanee el archivo y vea si está allí.

Con 512 MB de RAM y un archivo de paso único: marque cada número (= bit establecido en ese índice) leído del archivo, y luego pase la RAM una vez y vea los que faltan.

Eiko
fuente
1
Algunas buenas preguntas, pero si el sistema de 32 bits representa ints, flotantes o huzziwigs, solo puede representar 2 ^ 32 valores en 32 bits. Si la pregunta es "oh sí, permitimos 128 bits ultralargos", entonces la "restricción" de la arquitectura de 32 bits en la pregunta es deliberadamente engañosa. Aún así, una gran pregunta para el entrevistador, porque muchas especificaciones son engañosas o están mal escritas. Su solución real es un BitSet como el mío.
user949300
@ user949300 Sí, y es imposible saber qué está buscando el entrevistador. Si la última persona que contrataron fue un tipo de "pirateo de la pila antes de pensar", su respuesta debería ser diferente a si fuera un tipo "no tiene absolutamente ninguna idea sobre arquitectura" o "jugar al juego de optimización". :) He trabajado con grandes conjuntos de bits antes (aunque no en Java), por lo que me vienen a la mente de forma natural. Y también se puede adoptar para una memoria más baja si es necesario (almacenamiento). Los conjuntos de bits también resuelven el "problema de clasificación" en los comentarios anteriores en tiempo lineal con 512 MB de RAM.
Eiko
0

Un enfoque que es fácil de recordar y fácil de articular en una entrevista sería utilizar el hecho de que si observa todos los números en N bits, cada bit se establecerá exactamente en la mitad de esos valores y no en la otra mitad .

Si itera sobre todos los valores en el archivo y mantiene 32 recuentos de los valores al final, terminará con 32 valores que son exactamente (2 ^ 32/2) o un poco menos que ese valor. La diferencia entre el máximo (2 ^ 32/2) y el total le da el total de bits establecidos en cada posición de los valores faltantes.

Una vez que tenga eso, puede determinar todos los conjuntos posibles de 4 valores que podrían dar esos totales. Dado eso, puede revisar los valores en el archivo nuevamente buscando cualquier valor que sea parte de esas combinaciones. Cuando encuentra uno, las combinaciones que contienen ese valor se eliminan como posibilidades. Una vez que solo le quede una combinación posible, tendrá su respuesta.

Por ejemplo, usando un mordisco, tiene los siguientes valores:

1010
0110
1111
0111
1101
1001
0100
0101
0001
1011
1100
1110

El total de bits establecidos en cada posición es:

7867

Restando los de 8 (4 ^ 2/2) obtenemos:

1021

Lo que significa que existen los siguientes conjuntos posibles de 4 valores:

1000
0000
0011
0010

1010
0001
0010
0000

(perdóname si me he perdido alguno, solo estoy haciendo esto de vista)

Y luego, mirando nuevamente los números originales, encontramos 1010 de inmediato, lo que significa que el primer conjunto fue la respuesta.

JimmyJames
fuente
pero tienes que encontrar 4 números, no uno
freedev
@freedev Tienes razón. Eso es lo que hace. Un conjunto de cuatro números es cuatro números ... en un conjunto.
JimmyJames
Interesante, pero pasas por alto determine all the possible sets of 4 values that could give those totals. Realmente creo que esta es una parte importante de la solución que falta en su respuesta. También puede afectar la complejidad del tiempo y el espacio.
Allon Guralnek
@AllonGuralnek Tienes razón. Pasé un poco de tiempo trabajando en esto y había subestimado enormemente cuántos conjuntos de 4 números se sumarían al mismo número en el peor de los casos. Creo que esta es una idea rescatable, pero es un poco más complicada de lo que he presentado aquí. Actualizaré con detalles más adelante. Agradezco los comentarios.
JimmyJames
0

Suponiendo que el archivo está ordenado por números crecientes:

Asegúrese de que la sangría contenga números (2³²-4).
Ahora, si el archivo estuviera completo (o si los 4 números faltantes fueran los 4 últimos), leer cualquier palabra en el archivo en la posición N devolvería el valor correspondiente N.

Utilice una búsqueda de dicotomía en las posiciones [0..2³²-4-1) para buscar el primer número no esperado X1.
Una vez que encuentre el primer número que falta, realice una búsqueda de dichtotomía nuevamente en las posiciones [X1 .. (2³²-4-1)] para encontrar el segundo que falta, X2: esta vez, leer una palabra en la posición N debería devolver el valor correspondiente N-1 si no hubiera más números faltantes (ya que pasó un número faltante).
Iterar igualmente para los dos números restantes. En la tercera iteración, leer la palabra en la posición N debería devolver N-2, y en la cuarta, debería devolver N-3.

Advertencia: no he probado esto. Pero creo que debería funcionar. :)

Ahora en la vida real, estoy de acuerdo con otras respuestas: las primeras preguntas serían sobre el medio ambiente. ¿Tenemos disponibilidad de RAM (cuánto), es el archivo en un dispositivo de almacenamiento de acceso directo, es una operación de una sola vez (no se requiere optimización) o crítica (cuenta de cada ciclo), tenemos una utilidad de clasificación externa disponible , etc.
Luego encuentre un compromiso aceptable para el contexto. Esto al menos muestra que comienza a analizar el problema antes de buscar un algoritmo.

filofel
fuente
-2

Como con todas las preguntas estándar, la solución es buscarlas en Google antes de la entrevista.

Esta pregunta y sus variaciones tienen una respuesta 'correcta' muy definida que involucra XORing todos los números. Se supone que muestra que entiendes índices en bases de datos o algo así. Por lo tanto, cero puntos para cualquier "podría funcionar pero no lo que dice en el papel" responden de inmediato

En el lado positivo, hay un conjunto finito de estas preguntas. Unas pocas horas de revisión te harán ver como un genio. Solo recuerda fingir que lo estás resolviendo en tu cabeza.

Editar. Ahh parece que para 4 hay un enfoque diferente que XOR

http://books.google.com/books?id=415loiMd_c0C&lpg=PP1&dq=muthukrishnan%20data%20stream%20algorithms&hl=el&pg=PA1#v=onepage&q=muthukrishnan%20data%20stream%20algorithms&f=false

Editar. Votantes: Esta es una solución de libro de texto O (n) publicada para el problema exacto establecido en el OP.

Ewan
fuente
1
Cabe destacar que este libro vinculado trata sobre el procesamiento continuo. En particular, el flujo de procesamiento dentro de las restricciones. Dicho esto, sin duda me gustaría creer que este es el origen de la cuestión de la OP ha visto, ya que es por lo demás bastante trivial. Más notablemente, en realidad no has respondido la pregunta. Tendrás un +1 de mi parte si puedes plantearlo de manera convincente como la pregunta "original" o "intencionada" y explicar la solución ... pero, esto no responde nada como está.
svidgen
1
Esta respuesta (en una entrevista) solo muestra que leíste el libro. Nada sobre tus habilidades o procesos de pensamiento. ¿Y cómo "googleas todas las preguntas estándar " antes de una entrevista? ¿Hay alguna lista finita de "todas las preguntas formuladas en una entrevista" que me perdí?
user949300
1
¡@ewan también subraya la dificultad de contratar a un buen candidato! Si los "buenos" simplemente están bien preparados para las preguntas de la entrevista ... ¿Se vuelve difícil contratar a alguien que realmente pueda resolver mis problemas comerciales?
svidgen
1
@ewan Para ser claros, me estaba burlando de mi puntuación incorrecta. ... En cualquier caso, tenga en cuenta que también he recibido una buena cantidad de ofertas de trabajo en mi día, incluso ignorando bastante las preguntas y respuestas estándar como esta. Y ahora, como gerente de contratación, puedo prometerle que no quiero respuestas recitadas ... Sin embargo, entiendo que algunos gerentes tendrán diferentes necesidades.
svidgen
1
@Ewan También debería aclarar una cosa más, si mi tono no se recibió como se esperaba: debe revisar su respuesta para afirmar que el problema en el libro vinculado es la "pregunta prevista". Y luego responde la pregunta! ... Sin duda tendrías mi +1, y muchos otros, y la satisfacción de ayudar al OP para hacerlo.
svidgen