Dada una matriz de enteros, A 1 , A 2 , ..., A n , incluidos los negativos y positivos, y otro entero S. Ahora necesitamos encontrar tres enteros diferentes en la matriz, cuya suma es más cercana al entero dado S Si existe más de una solución, cualquiera de ellas está bien.
Puede suponer que todos los enteros están dentro del rango int32_t, y no se producirá un desbordamiento aritmético al calcular la suma. S no es nada especial sino un número elegido al azar.
¿Existe algún algoritmo eficiente que no sea la búsqueda de fuerza bruta para encontrar los tres enteros?
Respuestas:
Sí; ¡podemos resolver esto en O (n 2 ) tiempo! Primero, considere que su problema
P
puede expresarse de manera equivalente de una manera ligeramente diferente que elimine la necesidad de un "valor objetivo":Tenga en cuenta que se puede pasar de esta versión del problema
P'
delP
restando su S / 3 de cada elementoA
, pero ahora no es necesario el valor objetivo más.Claramente, si simplemente probamos todas las 3 tuplas posibles, resolveríamos el problema en O (n 3 ), esa es la línea base de fuerza bruta. ¿Es posible hacerlo mejor? ¿Qué pasa si elegimos las tuplas de una manera algo más inteligente?
Primero, invertimos algo de tiempo para ordenar la matriz, lo que nos cuesta una penalización inicial de O (n log n). Ahora ejecutamos este algoritmo:
Este algoritmo funciona mediante la colocación de tres puntos,
i
,j
, yk
en varios puntos de la matriz.i
comienza al principio y avanza lentamente hasta el final.k
apunta al último elemento.j
señala dóndei
ha comenzado. Intentamos sumar iterativamente los elementos en sus respectivos índices, y cada vez que ocurre uno de los siguientes:j
más cerca del final para seleccionar el siguiente número más grande.k
más cerca del comienzo para seleccionar el siguiente número más pequeño.Para cada uno
i
, los punteros dej
yk
gradualmente se acercarán entre sí. Eventualmente se pasarán entre sí, y en ese punto no necesitamos probar nada más para esoi
, ya que estaríamos sumando los mismos elementos, solo en un orden diferente. Después de ese punto, intentamos el siguientei
y repetimos.Finalmente, agotaremos las posibilidades útiles o encontraremos la solución. Puede ver que esto es O (n 2 ) ya que ejecutamos el bucle externo O (n) veces y ejecutamos el bucle interno O (n) veces. Es posible hacer esto subcuadráticamente si te apetece, representando cada número entero como un vector de bits y realizando una transformación rápida de Fourier, pero eso está más allá del alcance de esta respuesta.
Nota: Debido a que esta es una pregunta de entrevista, he engañado un poco aquí: este algoritmo permite la selección del mismo elemento varias veces. Es decir, (-1, -1, 2) sería una solución válida, como lo sería (0, 0, 0). También encuentra solo las respuestas exactas , no la respuesta más cercana, como menciona el título. Como ejercicio para el lector, te dejaré descubrir cómo hacer que funcione solo con elementos distintos (pero es un cambio muy simple) y respuestas exactas (que también es un cambio simple).
fuente
Sin duda, esta es una mejor solución porque es más fácil de leer y, por lo tanto, menos propensa a errores. El único problema es que necesitamos agregar algunas líneas de código para evitar la selección múltiple de un elemento.
Otra solución O (n ^ 2) (mediante el uso de un hashset).
fuente
s2
podría ser un elemento ya seleccionado. Por ejemplo, si la matriz es0,1,2
yK
es2
, no debería haber una respuesta. Creo que su algoritmo saldrá, lo0,1,1
que obviamente es incorrecto.La solución de John Feminella tiene un error.
En la linea
Necesitamos verificar si i, j, k son todos distintos. De lo contrario, si mi elemento de destino es
6
y si mi matriz de entrada contiene{3,2,1,7,9,0,-4,6}
. Si imprimo las tuplas que suman 6, entonces también obtendría0,0,6
como salida. Para evitar esto, necesitamos modificar la condición de esta manera.fuente
¿Qué tal algo como esto, que es O (n ^ 2)
Esto determina si la suma de 3 elementos es exactamente igual a su número. Si desea lo más cercano, puede modificarlo para recordar el delta más pequeño (diferencia entre su número de triplete actual) y al final imprimir el triplete correspondiente al delta más pequeño.
fuente
Tenga en cuenta que tenemos una matriz ordenada. Esta solución es similar a la solución de John solo que busca la suma y no repite el mismo elemento.
fuente
a[r] + a[l] + a[i] - sum
. Pruébatearr = [-1, 2, 1, -4] sum = 1
.Aquí está el código C ++:
fuente
Solución muy simple de N ^ 2 * logN: clasifique la matriz de entrada, luego revise todos los pares A i , A j (tiempo N ^ 2), y para cada par verifique si (S - A i - A j ) está en la matriz ( logN time).
Otra solución O (S * N) usa programación dinámica clásica enfoque .
En breve:
Cree una matriz 2-d V [4] [S + 1]. Llénalo de tal manera que:
V [0] [0] = 1, V [0] [x] = 0;
V 1 [A i ] = 1 para cualquier i, V 1 [x] = 0 para todos los demás x
V [2] [A i + A j ] = 1, para cualquier i, j. V [2] [x] = 0 para todos los demás x
V [3] [suma de 3 elementos] = 1.
Para llenarlo, itere a través de A i , para cada A i itere a través de la matriz de derecha a izquierda.
fuente
Esto se puede resolver de manera eficiente en O (n log (n)) de la siguiente manera. Estoy dando una solución que dice si la suma de tres números es igual a un número dado.
fuente
leftIndex
orightIndex
cuando todos los elementos en el medio son estrictamente más pequeños o más grandes que su número deseado. Pero, ¿qué pasa con el caso cuando la búsqueda binaria se detuvo en algún punto intermedio? Debería verificar ambas ramas (dónderightIndex--
yleftIndex++
). En su solución, simplemente ignora esta situación. Pero no creo que haya una manera de superar este problema.Reducción: creo que la solución @John Feminella O (n2) es muy elegante. Todavía podemos reducir la A [n] en la que buscar tuplas. Al observar A [k] tal que todos los elementos estarían en A [0] - A [k], cuando nuestra matriz de búsqueda es enorme y SUM (s) realmente pequeña.
A [0] es mínimo: - Matriz ordenada ascendente.
s = 2A [0] + A [k]: Dados sy A [] podemos encontrar A [k] usando la búsqueda binaria en el tiempo log (n).
fuente
Aquí está el programa en Java que es O (N ^ 2)
fuente
El problema se puede resolver en O (n ^ 2) extendiendo el problema de 2 sumas con modificaciones menores. A es el vector que contiene elementos y B es la suma requerida.
Solución int :: threeSumClosest (vector & A, int B) {
fuente
Aquí está el código Python3
fuente
Otra solución que verifica y falla temprano:
Agregué algunas pruebas unitarias aquí: GivenArrayReturnTrueIfThreeElementsSumZeroTest .
Si el conjunto usa demasiado espacio, puedo usar fácilmente un java.util.BitSet que usará espacio O (n / w) .
fuente
Programa para obtener esos tres elementos. Acabo de ordenar la matriz / lista primero y las actualicé en
minCloseness
función de cada triplete.fuente
Hice esto en n ^ 3, mi pseudocódigo está debajo;
// Cree un hashMap con la clave como Integer y el valor como ArrayList // itere a través de la lista usando un bucle for, para cada valor en la lista itere nuevamente comenzando desde el siguiente valor;
// si la suma de arr [i] y arr [j] es menor que la suma deseada, entonces existe la posibilidad de encontrar un tercer dígito, así que haga otro bucle for
// en este caso ahora estamos buscando el tercer valor; si la suma de arr [i] y arr [j] y arr [k] es la suma deseada, entonces agréguelas al HashMap haciendo que arr [i] sea la clave y luego agregue arr [j] y arr [k] en ArrayList en el valor de esa clave
después de esto, ahora tiene un diccionario que tiene todas las entradas que representan los tres valores que se suman a la suma deseada. Extraiga todas estas entradas utilizando las funciones de HashMap. Esto funcionó perfectamente.
fuente