Eu tive uma experiência interessante de entrevista de emprego há um tempo. A pergunta começou muito fácil:
Q1 : Temos um saco contendo números
1
,2
,3
, ...,100
. Cada número aparece exatamente uma vez, então existem 100 números. Agora, um número é escolhido aleatoriamente da sacola. Encontre o número que falta.
Já ouvi essa pergunta da entrevista antes, é claro, então respondi muito rapidamente ao longo das linhas de:
A1 : Bem, a soma dos números
1 + 2 + 3 + … + N
é(N+1)(N/2)
(veja Wikipedia: soma das séries aritméticas ). PoisN = 100
a soma é5050
.Assim, se todos os números estiverem presentes na sacola, a soma será exatamente
5050
. Como falta um número, a soma será menor que isso e a diferença é esse número. Para que possamos encontrar esse número ausente noO(N)
tempo e noO(1)
espaço.
Nesse ponto, pensei ter me saído bem, mas, de repente, a pergunta deu uma guinada inesperada:
P2 : Isso está correto, mas agora como você faria isso se dois números estiverem faltando?
Eu nunca tinha visto / ouvido / considerado essa variação antes, então entrei em pânico e não consegui responder à pergunta. O entrevistador insistiu em conhecer meu processo de pensamento, então mencionei que talvez possamos obter mais informações comparando com o produto esperado, ou talvez fazendo uma segunda passagem depois de coletar algumas informações da primeira passagem, etc., mas eu realmente estava apenas filmando no escuro, em vez de realmente ter um caminho claro para a solução.
O entrevistador tentou me encorajar dizendo que ter uma segunda equação é realmente uma maneira de resolver o problema. Nesse ponto, fiquei meio chateado (por não saber a resposta antes) e perguntei se essa é uma técnica de programação geral (leia-se: "útil"), ou se é apenas uma resposta de truque / pegadinha.
A resposta do entrevistador me surpreendeu: você pode generalizar a técnica para encontrar três números ausentes. De fato, você pode generalizá-lo para encontrar k números ausentes.
Qk : Se exatamente faltam k números da sacola, como você os encontrará com eficiência?
Isso foi há alguns meses atrás e eu ainda não conseguia descobrir o que é essa técnica. Obviamente há um Ω(N)
tempo limite inferior, uma vez que deve verificar todos os números, pelo menos uma vez, mas o entrevistador insistiu que o TEMPO e ESPAÇO complexidade da técnica de resolução (menos a O(N)
varredura de entrada tempo) é definida em k não N .
Portanto, a pergunta aqui é simples:
- Como você resolveria o Q2 ?
- Como você resolveria o terceiro trimestre ?
- Como você resolveria o Qk ?
Esclarecimentos
- Geralmente existem N números de 1 .. N , não apenas 1..100.
- Não estou procurando a solução óbvia baseada em conjunto, por exemplo, usando um conjunto de bits , codificando a presença / ausência de cada número pelo valor de um bit designado, usando
O(N)
bits em espaço adicional. Não podemos permitir qualquer proporcional de espaço adicional para N . - Também não estou procurando a abordagem óbvia do tipo primeiro. Vale a pena mencionar isso e a abordagem baseada em conjuntos em uma entrevista (eles são fáceis de implementar e, dependendo de N , podem ser muito práticos). Estou procurando a solução do Santo Graal (que pode ou não ser prática de implementar, mas tem as características assintóticas desejadas).
Então, novamente, é claro que você deve escanear a entrada O(N)
, mas você pode capturar apenas uma pequena quantidade de informação (definida em termos de k e não N ) e, em seguida, deve encontrar os k números ausentes de alguma forma.
XOR
todos os números de1
até en
, em seguida, obter resultados com todos os números na matriz fornecida. No final, você tem o seu número ausente. Nesta solução, você não precisa se preocupar com o estouro, como na síntese.Respostas:
Aqui está um resumo do link de Dimitris Andreou .
Lembre-se da soma dos i-ésimos poderes, onde i = 1,2, .., k. Isso reduz o problema para resolver o sistema de equações
a 1 + a 2 + ... + a k = b 1
a 1 2 + a 2 2 + ... + a k 2 = b 2
...
a 1 k + a 2 k + ... + a k k = b k
Usando as identidades de Newton , saber b i permite calcular
c 1 = a 1 + a 2 + ... a k
c 2 = a 1 a 2 + a 1 a 3 + ... + a k-1 a k
...
c k = a 1 a 2 ... a k
Se você expandir o polinômio (xa 1 ) ... (xa k ), os coeficientes serão exatamente c 1 , ..., c k - veja as fórmulas de Viète . Como todos os fatores polinomiais são únicos (o anel de polinômios é um domínio euclidiano ), isso significa que i são determinados exclusivamente, até a permutação.
Isso termina uma prova de que lembrar poderes é suficiente para recuperar os números. Para constante k, essa é uma boa abordagem.
No entanto, quando k está variando, a abordagem direta da computação c 1 , ..., c k é proibitivamente cara, pois, por exemplo, c k é o produto de todos os números ausentes, magnitude n! / (Nk) !. Para superar isso, faça cálculos no campo Z q , onde q é um primo tal que n <= q <2n - existe pelo postulado de Bertrand . A prova não precisa ser alterada, pois as fórmulas ainda se mantêm e a fatoração de polinômios ainda é única. Você também precisa de um algoritmo para fatorar sobre campos finitos, por exemplo, o de Berlekamp ou Cantor-Zassenhaus .
Pseudocódigo de alto nível para constante k:
Para variar k, encontre um primo n <= q <2n usando, por exemplo, Miller-Rabin, e execute as etapas com todos os números de módulo reduzido q.
EDIT: A versão anterior desta resposta afirmou que, em vez de Z q , onde q é primo, é possível usar um campo finito da característica 2 (q = 2 ^ (log n)). Não é esse o caso, uma vez que as fórmulas de Newton requerem divisão por números até k.
fonte
q = 2^(log n)
. (Como você fez as super e subscritos ?!)O(N^2)
solução mais trivial provavelmente superará essa beleza por até razoavelmente altaN
. Me faz pensar nisso: tinyurl.com/c8fwgw No entanto, um ótimo trabalho! Eu não teria tido a paciência para rastreamento através de toda a matemática :)hash set
e fazer iterações no1...N
conjunto usando pesquisas para determinar se os números estão faltando seria o mais genérico, o mais rápido em média em relação àsk
variações, a solução mais debugável, mais sustentável e compreensível. É claro que o caminho da matemática é impressionante, mas em algum ponto do caminho você precisa ser um engenheiro e não um matemático. Especialmente quando os negócios estão envolvidos.Você o encontrará lendo as duas páginas de Muthukrishnan - Algoritmos de fluxo de dados: Enigma 1: Encontrando números ausentes . Mostra exatamente a generalização que você está procurando . Provavelmente é isso que o entrevistador leu e por que ele fez essas perguntas.
Agora, se apenas as pessoas começassem a excluir as respostas que foram substituídas ou substituídas pelo tratamento de Muthukrishnan, e tornassem esse texto mais fácil de encontrar. :)
Veja também a resposta diretamente relacionada ao sdcvvc , que também inclui o pseudocódigo (viva! Não é necessário ler essas formulações matemáticas complicadas :)) (obrigado, ótimo trabalho!).
fonte
Podemos resolver o Q2 somando os números em si e os quadrados dos números.
Podemos então reduzir o problema para
Onde
x
e ondey
estão as somas abaixo dos valores esperados.Substituir nos dá:
Que podemos resolver para determinar nossos números ausentes.
fonte
Como @j_random_hacker apontou, isso é bastante semelhante a Encontrar duplicatas no tempo O (n) e no espaço O (1) , e uma adaptação da minha resposta aqui também funciona aqui.
Supondo que a "bolsa" seja representada por uma matriz
A[]
de tamanho baseada em 1N - k
, podemos resolver Qk noO(N)
tempo e noO(k)
espaço adicional.Primeiro, estendemos nossa matriz
A[]
pork
elementos, para que ela agora tenha tamanhoN
. Este é oO(k)
espaço adicional. Em seguida, executamos o seguinte algoritmo de pseudo-código:O primeiro loop inicializa as
k
entradas extras da mesma forma que a primeira entrada na matriz (este é apenas um valor conveniente que sabemos que já está presente na matriz - após esta etapa, todas as entradas que estavam faltando na matriz inicial de tamanhoN-k
são ainda falta na matriz estendida).O segundo loop permite a matriz estendida, de modo que, se o elemento
x
estiver presente pelo menos uma vez, uma dessas entradas estará na posiçãoA[x]
.Observe que, embora tenha um loop aninhado, ele ainda é executado no
O(N)
tempo - uma troca ocorre apenas se houver umai
tal queA[i] != i
, e cada troca define pelo menos um elemento como aqueleA[i] == i
, onde isso não era verdade antes. Isso significa que o número total de swaps (e, portanto, o número total de execuções dowhile
corpo do loop) é no máximoN-1
.O terceiro loop imprime os índices da matriz
i
que não são ocupados pelo valori
- isso significa quei
deve estar faltando.fonte
A[i]
, o que significa que a próxima iteração não comparará os mesmos dois valores que o anterior. O novoA[i]
será o mesmo do último loopA[A[i]]
, mas o novoA[A[i]]
será um novo valor. Experimente e veja.Pedi a uma criança de 4 anos para resolver este problema. Ele ordenou os números e depois contou. Isso requer um espaço necessário de O (piso da cozinha) e funciona tão facilmente quanto muitas bolas faltam.
fonte
Não tenho certeza, se é a solução mais eficiente, mas eu repetiria todas as entradas e usaria um conjunto de bits para lembrar quais números estão definidos e, em seguida, testaria 0 bits.
Gosto de soluções simples - e até acredito que pode ser mais rápido que calcular a soma, a soma dos quadrados etc.
fonte
O(N)
tipo de contagem nem o tipo deO(N log N)
comparação é o que estou procurando, embora sejam soluções muito simples.Não verifiquei a matemática, mas suspeito que a computação
Σ(n^2)
no mesmo passo em que calculamosΣ(n)
forneceria informações suficientes para obter dois números ausentes, façaΣ(n^3)
o mesmo se houver três e assim por diante.fonte
O problema com soluções baseadas em somas de números é que elas não levam em conta o custo de armazenar e trabalhar com números com grandes expoentes ... na prática, para que funcione para n muito grande, seria usada uma biblioteca de grandes números . Podemos analisar a utilização do espaço para esses algoritmos.
Podemos analisar a complexidade de tempo e espaço dos algoritmos sdcvvc e Dimitris Andreou.
Armazenamento:
assim
l_j \in \Theta(j log n)
Armazenamento total usado:
\sum_{j=1}^k l_j \in \Theta(k^2 log n)
Espaço utilizado: assumindo que a computação
a^j
levaceil(log_2 j)
tempo, tempo total:Tempo total usado:
\Theta(kn log n)
Se esse tempo e espaço forem satisfatórios, você pode usar um algoritmo recursivo simples. Seja b! A i-ésima entrada na sacola, no número de números antes das remoções ek no número de remoções. Na sintaxe Haskell ...
Armazenamento usado:
O(k)
para lista,O(log(n))
para pilha:O(k + log(n))
esse algoritmo é mais intuitivo, tem a mesma complexidade de tempo e usa menos espaço.fonte
isInRange
é O (log n) , não O (1) : compara números no intervalo 1..n, portanto, ele precisa comparar os bits O (log n) . Não sei até que ponto esse erro afeta o restante da análise.Espere um minuto. Como a pergunta é declarada, há 100 números na sacola. Não importa o tamanho de k, o problema pode ser resolvido em tempo constante, porque você pode usar um conjunto e remover números do conjunto em no máximo 100 k de iterações de um loop. 100 é constante. O conjunto de números restantes é a sua resposta.
Se generalizarmos a solução para os números de 1 a N, nada muda, exceto N não é uma constante, então estamos no tempo O (N - k) = O (N). Por exemplo, se usarmos um conjunto de bits, configuramos os bits para 1 em O (N), iteramos pelos números, configurando os bits para 0 à medida que avançamos (O (Nk) = O (N)) e depois tem a resposta.
Parece-me que o entrevistador estava perguntando como imprimir o conteúdo do conjunto final no tempo O (k) em vez do tempo O (N). Claramente, com um pouco definido, você precisa percorrer todos os N bits para determinar se deve imprimir o número ou não. No entanto, se você alterar a maneira como o aparelho é implementado, poderá imprimir os números em k iterações. Isso é feito colocando os números em um objeto a ser armazenado em um conjunto de hash e em uma lista duplamente vinculada. Quando você remove um objeto do conjunto de hash, também o remove da lista. As respostas serão deixadas na lista que agora tem o comprimento k.
fonte
Para resolver a questão de 2 (e 3) números ausentes, você pode modificar
quickselect
, que em média é executadoO(n)
e usa memória constante se o particionamento for feito no local.Particione o conjunto com relação a um pivô aleatório
p
em partiçõesl
, que contêm números menores que o pivô er
, que contêm números maiores que o pivô.Determine em quais partições os 2 números ausentes estão comparando o valor de pivô ao tamanho de cada partição (
p - 1 - count(l) = count of missing numbers in l
en - count(r) - p = count of missing numbers in r
)a) Se estiver faltando um número em cada partição, use a abordagem da diferença de somas para encontrar cada número que falta.
(1 + 2 + ... + (p-1)) - sum(l) = missing #1
e((p+1) + (p+2) ... + n) - sum(r) = missing #2
b) Se uma partição está faltando os dois números e a partição está vazia, então os números ausentes estão
(p-1,p-2)
ou(p+1,p+2)
dependem de qual partição está faltando os números.Se uma partição estiver faltando 2 números, mas não estiver vazia, recorra a essa partição.
Com apenas 2 números ausentes, esse algoritmo sempre descarta pelo menos uma partição, mantendo a
O(n)
complexidade do tempo médio da seleção rápida. Da mesma forma, com 3 números ausentes, esse algoritmo também descarta pelo menos uma partição a cada passagem (porque, como 2 números ausentes, no máximo apenas 1 partição conterá vários números ausentes). No entanto, não tenho certeza de quanto o desempenho diminui quando mais números ausentes são adicionados.Aqui está uma implementação que não usa particionamento no local, portanto, este exemplo não atende aos requisitos de espaço, mas ilustra as etapas do algoritmo:
Demo
fonte
Aqui está uma solução que usa k bits de armazenamento extra, sem truques inteligentes e simples. Tempo de execução O (n), espaço extra O (k). Apenas para provar que isso pode ser resolvido sem ler a solução primeiro ou ser um gênio:
fonte
(data [n - 1 - odd] % 2 == 1) ++odd;
?Você pode verificar se todos os números existem? Se sim, você pode tentar o seguinte:
se os números ausentes forem
x
ey
então:Então você verificar o intervalo de
1
paramax(x)
e encontrar o númerofonte
max(x)
significa quandox
é um número?Pode ser que esse algoritmo possa funcionar para a pergunta 1:
Ou melhor ainda:
Na verdade, esse algoritmo pode ser expandido para dois números ausentes. O primeiro passo permanece o mesmo. Quando chamamos GetValue com dois números ausentes, o resultado será a
a1^a2
são os dois números ausentes. Digamosval = a1^a2
Agora, para peneirar a1 e a2 de val, pegamos qualquer bit definido em val. Vamos dizer que o
ith
bit está definido em val. Isso significa que a1 e a2 têm paridade diferente naith
posição do bit. Agora fazemos outra iteração na matriz original e mantemos dois valores xor. Um para os números que têm o i-bit definido e outro que não tem o i-bit definido. Agora temos dois baldes de números, e é garantido quea1 and a2
estarão em baldes diferentes. Agora repita o mesmo que fizemos para encontrar um elemento ausente em cada um dos buckets.fonte
k=1
, certo? Mas eu gosto de usarxor
somas, parece um pouco mais rápido.Você pode resolver o Q2 se tiver a soma de ambas as listas e o produto de ambas.
(l1 é o original, l2 é a lista modificada)
Podemos otimizar isso, pois a soma de uma série aritmética é n vezes a média do primeiro e do último termos:
Agora sabemos que (se aeb são os números removidos):
Para que possamos reorganizar para:
E multiplique:
E reorganize para que o lado direito seja zero:
Então podemos resolver com a fórmula quadrática:
Exemplo de código Python 3:
Não conheço a complexidade do sqrt, reduzi e soquei as funções, por isso não consigo descobrir a complexidade desta solução (se alguém souber, por favor, comente abaixo).
fonte
x1*x2*x3*...
?Para o Q2, essa é uma solução um pouco mais ineficiente que as outras, mas ainda possui tempo de execução O (N) e ocupa espaço O (k).
A idéia é executar o algoritmo original duas vezes. No primeiro, você obtém um número total que está faltando, o que fornece um limite superior para os números ausentes. Vamos ligar para este número
N
. Você sabe que os dois números ausentes serão somadosN
; portanto, o primeiro número só pode estar no intervalo[1, floor((N-1)/2)]
enquanto o segundo está[floor(N/2)+1,N-1]
.Assim, você executa um loop em todos os números mais uma vez, descartando todos os números que não estão incluídos no primeiro intervalo. Os que são, você acompanha a soma deles. Por fim, você conhecerá um dos dois números ausentes e, por extensão, o segundo.
Tenho a sensação de que esse método pode ser generalizado e talvez várias pesquisas sejam executadas em "paralelo" durante uma única passagem pela entrada, mas ainda não descobri como.
fonte
Eu acho que isso pode ser feito sem quaisquer equações e teorias matemáticas complexas. Abaixo está uma proposta para uma solução no local e com complexidade de tempo O (2n):
Suposições do formulário de entrada:
Nº de números na bolsa = n
Nº de números ausentes = k
Os números na sacola são representados por uma matriz de comprimento n
Comprimento da matriz de entrada para o algo = n
As entradas ausentes na matriz (números retirados da sacola) são substituídas pelo valor do primeiro elemento na matriz.
Por exemplo. Inicialmente, o saco se parece com [2,9,3,7,8,6,4,5,1,10]. Se 4 for retirado, o valor de 4 se tornará 2 (o primeiro elemento da matriz). Portanto, depois de tirar 4, a bolsa ficará com [2,9,3,7,8,6,2,5,1,10]
A chave para esta solução é marcar o ÍNDICE de um número visitado, negando o valor nesse ÍNDICE à medida que a matriz é percorrida.
fonte
Existe uma maneira geral de generalizar algoritmos de streaming como este. A idéia é usar um pouco de randomização para "espalhar" os
k
elementos em subproblemas independentes, onde nosso algoritmo original resolve o problema para nós. Essa técnica é usada na reconstrução de sinais esparsos, entre outras coisas.a
de tamanhou = k^2
.h : {1,...,n} -> {1,...,u}
. (Como turno múltiplo )i
em1, ..., n
aumentoa[h(i)] += i
x
no fluxo de entrada, diminuaa[h(x)] -= x
.Se todos os números ausentes tiverem um hash em intervalos diferentes, os elementos diferentes de zero da matriz agora conterão os números ausentes.
A probabilidade de um determinado par ser enviado para o mesmo bucket é menor do que
1/u
por definição de uma função hash universal. Como existemk^2/2
pares, temos que a probabilidade de erro seja no máximok^2/2/u=1/2
. Ou seja, obtemos sucesso com probabilidade de pelo menos 50% e, se aumentarmosu
, aumentamos nossas chances.Observe que esse algoritmo ocupa
k^2 logn
bits de espaço (precisamos delogn
bits por intervalo de array). Isso corresponde ao espaço exigido pela resposta de @Dimitris Andreou (em particular, o requisito de espaço da fatoração polinomial, que também é randomizado.) Esse algoritmo também tem constante tempo por atualização, em vez de tempok
no caso de somas de energia.De fato, podemos ser ainda mais eficientes do que o método da soma de potência, usando o truque descrito nos comentários.
fonte
xor
em cada balde, se nãosum
for mais rápido em nossa máquina.k <= sqrt(n)
- pelo menos seu=k^2
? Suponha que k = 11 e n = 100, então você teria 121 buckets e o algoritmo acabaria sendo semelhante a ter uma matriz de 100 bits que você desmarca ao ler cada # do fluxo. Aumentaru
aumenta as chances de sucesso, mas há um limite para quanto você pode aumentá-lo antes de exceder a restrição de espaço.n
muito maior do quek
, eu acho, mas você pode reduzir o espaçok logn
com um método muito semelhante ao hash descrito, enquanto mantém atualizações constantes de tempo. É descrito em gnunet.org/eppstein-set-reconciliation , como o método da soma das potências, mas basicamente você faz hash em 'dois de k' buckets com uma forte função hash como tabulation hashing, o que garante que algum bucket terá apenas um elemento . Para decodificar, identifica essa balde e remove o elemento de ambos os seus baldes, que (provavelmente) libera outro balde e assim por dianteUma solução muito simples para o segundo trimestre, que estou surpresa que ninguém tenha respondido. Use o método da Q1 para encontrar a soma dos dois números ausentes. Vamos denotá-lo por S, então um dos números ausentes é menor que S / 2 e o outro é maior que S / 2 (duh). Soma todos os números de 1 a S / 2 e compare-o com o resultado da fórmula (da mesma forma que no método Q1) para encontrar o menor entre os números ausentes. Subtraia-o de S para encontrar o maior número ausente.
fonte
Problema muito bom. Eu usaria uma diferença definida para Qk. Muitas linguagens de programação ainda têm suporte para isso, como no Ruby:
Provavelmente não é a solução mais eficiente, mas é uma que eu usaria na vida real se me desse conta de uma tarefa nesse caso (limites conhecidos, limites baixos). Se o conjunto de números fosse muito grande, eu consideraria um algoritmo mais eficiente, é claro, mas até então a solução simples seria suficiente para mim.
fonte
Você pode tentar usar um filtro Bloom . Insira cada número no saco na flor e, em seguida, repita o conjunto completo de 1k até relatar que cada um não foi encontrado. Isso pode não encontrar a resposta em todos os cenários, mas pode ser uma solução boa o suficiente.
fonte
Eu adotaria uma abordagem diferente para essa pergunta e sondaria o entrevistador para obter mais detalhes sobre o problema maior que ele está tentando resolver. Dependendo do problema e dos requisitos que o cercam, a solução óbvia baseada em conjunto pode ser a coisa certa e a abordagem gerar-uma-lista-e-escolher-depois-depois.
Por exemplo, pode ser que o entrevistador despache
n
mensagens e precise saber ok
que não resultou em uma resposta e precise conhecê-la no menor tempo possível após o relógion-k
resposta. Digamos também que a natureza do canal de mensagens é tal que, mesmo funcionando em plena capacidade, há tempo suficiente para processar as mensagens sem afetar o tempo necessário para produzir o resultado final após a chegada da última resposta. Esse tempo pode ser usado para inserir algumas facetas identificadoras de cada mensagem enviada em um conjunto e excluí-la quando cada resposta correspondente chegar. Depois que a última resposta chega, a única coisa a ser feita é remover seu identificador do conjunto, o que em implementações típicas levaO(log k+1)
. Depois disso, o conjunto contém a lista dek
elementos ausentes e não há processamento adicional a ser feito.Essa certamente não é a abordagem mais rápida para processar lotes de números pré-gerados, porque tudo funciona
O((log 1 + log 2 + ... + log n) + (log n + log n-1 + ... + log k))
. Mas funciona com qualquer valor dek
(mesmo que não seja conhecido antecipadamente) e, no exemplo acima, foi aplicado de maneira a minimizar o intervalo mais crítico.fonte
Você pode motivar a solução pensando nela em termos de simetrias (grupos, na linguagem matemática). Não importa a ordem do conjunto de números, a resposta deve ser a mesma. Se você for usar
k
funções para ajudar a determinar os elementos ausentes, pense em quais funções têm essa propriedade: simétrica. A funçãos_1(x) = x_1 + x_2 + ... + x_n
é um exemplo de função simétrica, mas existem outras de maior grau. Em particular, considere as funções simétricas elementares . A função simétrica elementar do grau 2 és_2(x) = x_1 x_2 + x_1 x_3 + ... + x_1 x_n + x_2 x_3 + ... + x_(n-1) x_n
a soma de todos os produtos de dois elementos. Da mesma forma, para as funções simétricas elementares de grau 3 e superior. Eles são obviamente simétricos. Além disso, eles são os blocos de construção para todas as funções simétricas.Você pode criar as funções simétricas elementares conforme observa isso
s_2(x,x_(n+1)) = s_2(x) + s_1(x)(x_(n+1))
. Uma reflexão mais aprofundada deve convencê-lo dissos_3(x,x_(n+1)) = s_3(x) + s_2(x)(x_(n+1))
e assim por diante, para que eles possam ser computados de uma só vez.Como podemos saber quais itens estavam faltando na matriz? Pense no polinômio
(z-x_1)(z-x_2)...(z-x_n)
. Ele avalia0
se você inseriu algum dos númerosx_i
. Expandindo o polinômio, você entendez^n-s_1(x)z^(n-1)+ ... + (-1)^n s_n
. As funções simétricas elementares também aparecem aqui, o que não é realmente uma surpresa, uma vez que o polinômio deve permanecer o mesmo se aplicarmos alguma permutação nas raízes.Assim, podemos construir o polinômio e tentar fatorá-lo para descobrir quais números não estão no conjunto, como outros já mencionaram.
Por fim, se estivermos preocupados com o transbordamento de memória com grandes números (o enésimo polinômio simétrico será da ordem
100!
), podemos fazer esses cálculosmod p
ondep
é um primo maior que 100. Nesse caso, avaliamos o polinômiomod p
e descobrimos que ele novamente avalia para0
quando a entrada é um número no conjunto e é avaliado como um valor diferente de zero quando a entrada é um número que não está no conjunto. No entanto, como outros já apontaram, para obter os valores do polinômio no tempo que dependek
, nãoN
, precisamos fatorar o polinômiomod p
.fonte
Outra maneira é usar a filtragem de gráfico residual.
Suponha que temos os números 1 a 4 e 3 está faltando. A representação binária é a seguinte,
1 = 001b, 2 = 010b, 3 = 011b, 4 = 100b
E eu posso criar um fluxograma como o seguinte.
Observe que o gráfico de fluxo contém x nós, enquanto x é o número de bits. E o número máximo de arestas é (2 * x) -2.
Portanto, para números inteiros de 32 bits, será necessário espaço O (32) ou O (1).
Agora, se eu remover a capacidade de cada número a partir de 1,2,4, resta um gráfico residual.
Por fim, executarei um loop como o seguinte,
Agora, o resultado está em
result
números que também não estão faltando (falso positivo). Mas o <= (tamanho do resultado) <= n quando hák
elementos ausentes.Vou percorrer a lista fornecida uma última vez para marcar o resultado como ausente ou não.
Portanto, a complexidade do tempo será O (n).
Finalmente, é possível reduzir o número de falsos positivos (e o espaço necessário), tendo nós
00
,01
,11
,10
em vez de apenas0
e1
.fonte
Você provavelmente precisaria de esclarecimentos sobre o que O (k) significa.
Aqui está uma solução trivial para k arbitrário: para cada v em seu conjunto de números, acumule a soma de 2 ^ v. No final, o loop i de 1 a N. Se a soma AND bit a bit com 2 ^ i for zero, então i estará ausente. (Ou numericamente, se o piso da soma dividida por 2 ^ i for par
sum modulo 2^(i+1)) < 2^i
.)Fácil né? Tempo O (N), armazenamento O (1) e suporta k arbitrário.
Só que você está computando números enormes que, em um computador real, exigiriam espaço O (N). De fato, esta solução é idêntica a um vetor de bits.
Portanto, você pode ser inteligente e calcular a soma, a soma dos quadrados e a soma dos cubos ... até a soma de v ^ k, e fazer as matemáticas sofisticadas para extrair o resultado. Mas esses também são grandes números, o que levanta a questão: de que modelo abstrato de operação estamos falando? Quanto cabe no espaço O (1) e quanto tempo leva para resumir números do tamanho que você precisar?
fonte
Aqui está uma solução que não depende de matemática complexa como as respostas de sdcvvc / Dimitris Andreou, não altera a matriz de entrada como caf e o Coronel Panic, e não usa o conjunto de bits de tamanho enorme como Chris Lercher, JeremyP e muitos outros fizeram. Basicamente, comecei com a ideia de Svalorzen / Gilad Deutch para o Q2, generalizei para o caso comum Qk e implementei em Java para provar que o algoritmo funciona.
A ideia
Suponha que tenhamos um intervalo arbitrário I, do qual sabemos apenas que ele contém pelo menos um dos números ausentes. Após uma passagem através do feixe de entrada, olhando apenas para os números de Eu , que pode obter tanto a soma S e a quantidade Q de números em falta a partir de I . Fazemos isso simplesmente diminuindo o comprimento de I toda vez que encontramos um número de I (para obter Q ) e diminuindo a soma pré-calculada de todos os números em I pelo número encontrado toda vez (para obter S ).
Agora vamos olhar para S e Q . Se Q = 1 , isto significa que, em seguida, que contém apenas um dos números que faltam, e este número é claramente S . Marcamos I como concluído (é chamado de "inequívoco" no programa) e o deixamos de fora de uma análise mais aprofundada. Por outro lado, se Q> 1 , podemos calcular a média A = S / Q de números que faltam contida no I . Como todos os números são distintas, pelo menos uma de tais números é estritamente inferior a um e pelo menos um é estritamente maior que um . Agora dividimos eu em Aem dois intervalos menores, cada um dos quais contém pelo menos um número ausente. Observe que não importa a qual dos intervalos atribuímos A , caso seja um número inteiro.
Fazemos a próxima matriz passar calculando S e Q para cada um dos intervalos separadamente (mas na mesma passagem) e depois marcar intervalos com Q = 1 e dividir intervalos com Q> 1 . Continuamos esse processo até que não haja novos intervalos "ambíguos", ou seja, não temos nada para dividir porque cada intervalo contém exatamente um número ausente (e sempre o conhecemos porque conhecemos S ). Começamos a partir do único intervalo "todo o intervalo", contendo todos os números possíveis (como [1..N] na pergunta).
Análise de complexidade de tempo e espaço
O número total de passes p que precisamos fazer até o processo parar nunca é maior do que os números ausentes contam k . A desigualdade p <= k pode ser comprovada rigorosamente. Por outro lado, também existe um limite superior empírico p <log 2 N + 3 que é útil para grandes valores de k . Precisamos fazer uma pesquisa binária para cada número da matriz de entrada para determinar o intervalo ao qual ela pertence. Isso adiciona o multiplicador de log k à complexidade do tempo.
No total, a complexidade do tempo é O (N ᛫ min (k, log N) ᛫ log k) . Observe que, para k grande , isso é significativamente melhor que o método do sdcvvc / Dimitris Andreou, que é O (N ᛫ k) .
Por seu trabalho, o algoritmo requer O (k) espaço adicional para armazenar na maioria dos intervalos de k , o que é significativamente melhor que O (N) em soluções de "bitset".
Implementação Java
Aqui está uma classe Java que implementa o algoritmo acima. Ele sempre retorna uma matriz classificada de números ausentes. Além disso, ele não requer que os números ausentes contem k, porque o calcula na primeira passagem. Todo o intervalo de números é dado pelos parâmetros
minNumber
emaxNumber
(por exemplo, 1 e 100 para o primeiro exemplo da pergunta).Para ser justo, essa classe recebe entrada na forma de
NumberBag
objetos.NumberBag
não permite modificação de matriz e acesso aleatório e também conta quantas vezes a matriz foi solicitada para deslocamento sequencial. Também é mais adequado para testes de grandes matrizes do queIterable<Integer>
porque evita o encaixe deint
valores primitivos e permite envolver uma parte de um grandeint[]
para uma preparação de teste conveniente. Não é difícil substituir, se desejado,NumberBag
porint[]
ouIterable<Integer>
digitar afind
assinatura, alterando dois loops for para lo em foreach.Testes
Exemplos simples que demonstram o uso dessas classes são dados abaixo.
O teste de grandes matrizes pode ser realizado desta maneira:
Experimente-os no Ideone
fonte
Acredito que tenho um algoritmo de
O(k)
tempo eO(log(k))
espaço, já que você tem as funçõesfloor(x)
elog2(x)
para números inteiros arbitrariamente grandes disponíveis:Você tem um
k
número inteiro longo de bits (daí olog8(k)
espaço) onde você adiciona ox^2
, onde x é o próximo número que você encontra na sacola:s=1^2+2^2+...
isso levaO(N)
tempo (o que não é um problema para o entrevistador). No final, você obtémj=floor(log2(s))
qual é o maior número que você está procurando. Então,s=s-j
e você faz novamente o acima:Agora, você normalmente não possui as funções floor e log2 para
2756
números inteiros -bit, mas sim para duplos. Assim? Simplesmente, para cada 2 bytes (ou 1, 3 ou 4), você pode usar essas funções para obter os números desejados, mas isso adiciona umO(N)
fator à complexidade do tempofonte
Isso pode parecer estúpido, mas, no primeiro problema apresentado a você, você precisaria ver todos os números restantes na sacola para adicioná-los e encontrar o número que falta usando essa equação.
Portanto, como você vê todos os números, procure o número que está faltando. O mesmo vale para quando faltam dois números. Bem simples, eu acho. Não faz sentido usar uma equação quando você vê os números restantes na sacola.
fonte
Eu acho que isso pode ser generalizado assim:
Denota S, M como os valores iniciais para a soma das séries aritméticas e multiplicação.
Eu deveria pensar em uma fórmula para calcular isso, mas esse não é o ponto. De qualquer forma, se um número estiver faltando, você já forneceu a solução. No entanto, se dois números estiverem ausentes, vamos denotar a nova soma e o múltiplo total por S1 e M1, que serão os seguintes:
Como você conhece S1, M1, M e S, a equação acima é solucionável para encontrar aeb, os números ausentes.
Agora, para os três números ausentes:
Agora o seu desconhecido é 3, enquanto você só tem duas equações que você pode resolver.
fonte
M1 = M / (a * b)
(veja a resposta ). Então funciona bem.Não sei se isso é eficiente ou não, mas gostaria de sugerir esta solução.
4.Get a soma dos números ausentes com sua abordagem usual do soma fórmula diff e vamos dizer que o diff é d.
Agora execute um loop para obter os pares possíveis (p, q), ambos em [1, 100] e somar em d.
Quando um par é obtido, verifique se (resultado de 3) XOR p = q e se sim, terminamos.
Corrija-me se estiver errado e também comente a complexidade do tempo, se estiver correto
fonte
Podemos fazer o Q1 e Q2 em O (log n) na maioria das vezes.
Suponha que nosso
memory chip
consiste em matriz den
número detest tubes
. E um númerox
no tubo de ensaio é representado porx
milliliter
de químico-líquido.Suponha que nosso processador seja a
laser light
. Quando acendemos o laser, ele atravessa todos os tubos perpendicularmente ao seu comprimento. Sempre que passa pelo líquido químico, a luminosidade é reduzida em1
. E passar a luz em uma marca de mililitro é uma operação deO(1)
.Agora, se acendermos o laser no meio do tubo de ensaio e obtermos a saída de luminosidade
n/2
.n/2
. Também podemos verificar se a luminosidade é reduzida em1
ou2
. se for reduzido1
, um número ausente será menor quen/2
e outro será maior quen/2
. Se for reduzido2
, os dois números serão menores quen/2
.Podemos repetir o processo acima várias vezes, restringindo nosso domínio de problemas. Em cada etapa, reduzimos o domínio pela metade. E, finalmente, podemos chegar ao nosso resultado.
Algoritmos paralelos que merecem ser mencionados (por serem interessantes),
O(log^3 n)
tempo. E então o número ausente pode ser encontrado por busca binária noO(log n)
tempo.n
processadores, cada processo poderá verificar uma das entradas e definir algum sinalizador que identifique o número (convenientemente em uma matriz). E na próxima etapa, cada processo pode verificar cada sinalizador e finalmente gerar o número que não está sinalizado. Todo o processo levaráO(1)
tempo. PossuiO(n)
requisitos adicionais de espaço / memória.Observe que os dois algoritmos paralelos fornecidos acima podem precisar de espaço adicional, conforme mencionado no comentário .
fonte
O(logn)
em um computador.N
e mais do queO(N)
tempo (em termos de dependênciaN
), da qual pretendemos fazer melhor.