Dada uma matriz de números inteiros, A 1 , A 2 , ..., A n , incluindo negativos e positivos e outro número S. Agora precisamos encontrar três números inteiros diferentes na matriz, cuja soma é a mais próxima do número inteiro S Se houver mais de uma solução, qualquer uma delas está correta.
Você pode assumir que todos os números inteiros estão dentro do intervalo int32_t, e nenhum estouro aritmético ocorrerá com o cálculo da soma. S não é nada de especial, mas um número escolhido aleatoriamente.
Existe algum algoritmo eficiente além da pesquisa de força bruta para encontrar os três números inteiros?
Respostas:
Sim; podemos resolver isso em O (n 2 ) tempo! Primeiro, considere que seu problema
P
pode ser formulado de forma equivalente de uma maneira ligeiramente diferente que elimina a necessidade de um "valor-alvo":Observe que você pode ir a partir desta versão do problema
P'
deP
subtraindo o seu S / 3 de cada elementoA
, mas agora você não precisa o valor-alvo mais.Claramente, se simplesmente testássemos todas as três tuplas possíveis, resolveríamos o problema em O (n 3 ) - essa é a linha de base da força bruta. É possível fazer melhor? E se escolhermos as tuplas de uma maneira um pouco mais inteligente?
Primeiro, investimos algum tempo para classificar a matriz, o que nos custa uma penalidade inicial de O (n log n). Agora, executamos este algoritmo:
Este algoritmo funciona colocando três ponteiros,
i
,j
, ek
em vários pontos da matriz.i
começa no começo e lentamente segue seu caminho até o fim.k
aponta para o último elemento.j
aponta para ondei
começou. Tentamos iterativamente somar os elementos em seus respectivos índices e cada vez que acontece um dos seguintes:j
se do final para selecionar o próximo maior número.k
se do início para selecionar o próximo número menor.Para cada um
i
, os ponteirosj
ek
gradualmente se aproximam um do outro. Eventualmente, eles passarão um pelo outro e, nesse ponto, não precisamos tentar mais nada para issoi
, pois estaríamos somando os mesmos elementos, apenas em uma ordem diferente. Depois desse ponto, tentamos o próximoi
e repetimos.Eventualmente, esgotaremos as possibilidades úteis ou encontraremos a solução. Você pode ver que esse é O (n 2 ), pois executamos o loop externo O (n) vezes e executamos o loop interno O (n) vezes. É possível fazer isso sub-quadraticamente se você realmente gosta, representando cada número inteiro como um vetor de bits e realizando uma transformação rápida de Fourier, mas isso está além do escopo desta resposta.
Nota: Por se tratar de uma pergunta de entrevista, eu trapacei um pouco aqui: esse algoritmo permite a seleção do mesmo elemento várias vezes. Ou seja, (-1, -1, 2) seria uma solução válida, como seria (0, 0, 0). Ele também encontra apenas as respostas exatas , e não a mais próxima, como o título menciona. Como exercício para o leitor, deixarei você descobrir como fazê-lo funcionar apenas com elementos distintos (mas é uma mudança muito simples) e respostas exatas (que também são uma mudança simples).
fonte
certamente essa é uma solução melhor, porque é mais fácil de ler e, portanto, menos propensa a erros. O único problema é que precisamos adicionar algumas linhas de código para evitar a seleção múltipla de um elemento.
Outra solução O (n ^ 2) (usando um hashset).
fonte
s2
pode ser um elemento já selecionado. Por exemplo, se a matriz é0,1,2
eK
é2
, não deve haver uma resposta. Eu acho que o seu algoritmo irá mostrar o0,1,1
que está obviamente incorreto.A solução de John Feminella tem um bug.
Na linha
Precisamos verificar se i, j, k são todos distintos. Caso contrário, se meu elemento de destino for
6
e se minha matriz de entrada contiver{3,2,1,7,9,0,-4,6}
. Se eu imprimir as tuplas que somam 6, também obteria0,0,6
como saída. Para evitar isso, precisamos modificar a condição dessa maneira.fonte
Que tal algo assim, que é O (n ^ 2)
Isso verifica se a soma de 3 elementos é exatamente igual ao seu número. Se você quiser mais perto, pode modificá-lo para lembrar o menor delta (diferença entre o número de trigêmeos atual) e, no final, imprimir o trigêmeo correspondente ao menor delta.
fonte
Observe que temos uma matriz classificada. Essa solução é semelhante à solução de John, apenas que procura a soma e não repete o mesmo elemento.
fonte
a[r] + a[l] + a[i] - sum
. Experimentearr = [-1, 2, 1, -4] sum = 1
.Aqui está o código C ++:
fonte
Solução N ^ 2 * logN muito simples: classifique a matriz de entrada, depois passe por todos os pares A i , A j (hora N ^ 2) e, para cada par, verifique se (S - A i - A j ) está na matriz ( hora do logN).
Outra solução O (S * N) usa a abordagem de programação dinâmica clássica .
Em resumo:
Crie uma matriz 2-V V [4] [S + 1]. Preencha de tal maneira que:
V [0] [0] = 1, V [0] [x] = 0;
V 1 [A i ] = 1 para qualquer i, V 1 [x] = 0 para todos os outros x
V [2] [A i + A j ] = 1, para qualquer i, j. V [2] [x] = 0 para todos os outros x
V [3] [soma de quaisquer 3 elementos] = 1.
Para preenchê-lo, percorra A i , para cada A i percorra a matriz da direita para a esquerda.
fonte
Isso pode ser resolvido com eficiência em O (n log (n)) da seguinte maneira. Estou dando uma solução que diz se a soma de quaisquer três números é igual a um determinado número.
fonte
leftIndex
ourightIndex
quando todos os elementos no meio são estritamente menores ou maiores que o número desejado. Mas e o caso em que a pesquisa binária parou em algum lugar no meio? Você precisaria verificar os dois ramos (onderightIndex--
eleftIndex++
). Na sua solução, você simplesmente ignora esta situação. Mas não acho que exista uma maneira de superar esse problema.Redução: eu acho que a solução O (n2) da John Feminella é a mais elegante. Ainda podemos reduzir o A [n] no qual procurar tupla. Observando A [k] de modo que todos os elementos estejam em A [0] - A [k], quando nossa matriz de pesquisa é enorme e SUM (s) muito pequeno.
A [0] é mínimo: - Matriz ordenada crescente.
s = 2A [0] + A [k]: Dados s e A [], podemos encontrar A [k] usando pesquisa binária no log (n) tempo.
fonte
Aqui está o programa em java que é O (N ^ 2)
fonte
O problema pode ser resolvido em O (n ^ 2) estendendo o problema de 2 somas com pequenas modificações.A é o vetor que contém elementos e B é a soma necessária.
int Solution :: threeSumClosest (vetor e A, int B) {
fonte
Aqui está o código Python3
fonte
Outra solução que verifica e falha com antecedência:
Adicionei alguns testes de unidade aqui: GivenArrayReturnTrueIfThreeElementsSumZeroTest .
Se o conjunto está usando muito espaço I pode facilmente usar um java.util.BitSet que usará O (n / w) espaço .
fonte
Programa para obter esses três elementos. Acabei de classificar a matriz / lista primeiro e atualizá-las com
minCloseness
base em cada trigêmeo.fonte
Eu fiz isso em n ^ 3, meu pseudocódigo está abaixo;
// Crie um hashMap com chave como Inteiro e valor como ArrayList // itere na lista usando um loop for, para cada valor na lista itere novamente a partir do próximo valor;
// se a soma de arr [i] e arr [j] for menor que a soma desejada, é possível encontrar um terceiro dígito, assim como outro loop for
// neste caso, agora estamos procurando o terceiro valor; se a soma de arr [i] e arr [j] e arr [k] for a soma desejada, adicione-os ao HashMap, tornando o arr [i] a chave e adicionando arr [j] e arr [k] em o ArrayList no valor dessa chave
depois disso, você agora tem um dicionário que possui todas as entradas que representam os três valores adicionados à soma desejada. Extraia todas essas entradas usando as funções HashMap. Isso funcionou perfeitamente.
fonte