Você recebe um arquivo que contém todos os números possíveis em uma arquitetura de 32 bits. Faltam 4 números nesse arquivo. Encontre os 4 números que faltam

22

Esta é uma pergunta de entrevista que eu me deparei algumas vezes, e realmente não tenho certeza de como resolvê-la, pois faltam quatro números. Estou familiarizado com algoritmos para encontrar um ou dois números ausentes, mas não vejo uma maneira de generalizar nenhum deles para quatro.

Tsutarja47
fonte

Respostas:

19

Seja para uma entrevista ou para um trabalho real, sua primeira prioridade precisa ser uma solução funcional que faça sentido para você . Isso normalmente significa que você deve oferecer a primeira solução que você pode pensar que é simples e fácil para você explicar.

Para mim, isso significa classificar os números e procurar falhas. Mas trabalho em sistemas comerciais e aplicativos da web. Não brinco com pedaços, e não quero que minha equipe!

Se você estiver entrevistando para um trabalho de baixo nível e mais próximo do metal, a "classificação" provavelmente será vista com olhares em branco. Eles querem que você se sinta confortável com os bits e assim por diante. Sua primeira resposta deve ser "Ah, eu usaria um bitmap". (Ou matriz de bits ou conjunto de bits).

E então, de qualquer maneira - mesmo se você der uma solução "errada", se o entrevistador (ou o chefe!) Pressionar por isso , poderá sugerir algumas melhorias ou alternativas, concentrando-se na área de preocupação específica do gerente.

  • RAM gravemente limitada? Menos de 512MB?
    Classifique-o no lugar, no disco. Você pode usar uma quantidade de RAM principalmente arbitrária para otimizar e / ou armazenar em buffer os blocos classificados.
  • Tempo limitado?
    Use essa RAM! A classificação já está O(n*log(n)). (Ou O (n) para uma classificação de número inteiro!)
  • Manutenção?
    O que poderia ser mais fácil do que classificar ?!
  • Não demonstra conhecimento de sinalizadores / campos de bits? ( BitSet/ BitMap/ BitArray)
    Bem, OK ... vá em frente e use a BitArraypara sinalizar os "números encontrados". E então procure por 0.
  • Complexidade previsível em "tempo real"?
    Use a solução de bitmap. É uma única passagem sobre o arquivo e outra sobre oBitArray/BitSet(para encontrar os0). Isso éO(n), eu acho!

Como queiras.

Aborde as preocupações que você realmente tem. Apenas resolva o problema primeiro, usando soluções ingênuas, se necessário. Não perca o tempo de todos abordando preocupações que ainda não existem.

svidgen
fonte
Não tenho tanta certeza da viabilidade de classificar 4 bilhões de números com uma abordagem ingênua, quanto mais em disco. Nunca tentei, no entanto.
Eiko
1
@ Eiko Bem ... e novamente, o ponto principal é ... não complique demais as coisas. O primeiro passo é resolver o problema, da maneira que você pensar, mesmo que seja ingênuo. Não posso nem enfatizar o nível de frustração que seu futuro empregador terá se você estiver gastando tempo repetindo para se certificar de que tem a solução "certa" quando a empresa precisa apenas de uma solução. Prove que você pode fazer as duas coisas! Prove que você pode resolver os problemas rapidamente e, em seguida, identifique os possíveis problemas que vale a pena refatorar e / ou otimizar conforme necessário .
svidgen
1
@Ewan "Como você fez a pergunta na entrevista" não é o mesmo que "Há uma resposta específica que todo gerente está procurando". ... Eu certamente não me importaria com a solução que você me deu, desde que demonstrasse capacidade de resolver o problema e não fosse pego resolvendo problemas que nunca lhe dei!
Svidgen 17/10/16
1
Você está perdendo o ponto. Esta questão e suas variações ocorrem em livros de quebra-cabeças de programação e perguntas para entrevistas. Não foi inventado pela pessoa que fez a pergunta. o material de 32 bits deve tornar impossível fazer isso acompanhando os números ou a classificação. Seus computadores apenas ficaram mais rápidos / maiores desde que foram escritos.
Ewan
1
@ Ewan: você ainda está assumindo que sua instância da pergunta tem as mesmas restrições que os OPs. O OP não disse que seu algoritmo precisa rodar em uma máquina de 32 bits, ele nem mesmo disse que precisava rodar em um computador, um algoritmo conceitual poderia ser adequado. Ele também não indica o que significa "todos os números possíveis", pois é possível calcular números inteiros de tamanho arbitrário em microcontroladores de 8 bits. Muitas suposições que você está fazendo para dar declarações absolutas.
Whatsisname
19

Como é um arquivo, suponho que você tenha permissão para fazer várias passagens. Primeiro, crie uma matriz de 256 contadores, itere sobre o arquivo e para cada número incremente o contador indexado como o primeiro byte do número. Quando terminar, a maioria dos contadores deve estar em 2 ^ 24, mas 1 a 4 contadores devem ter valores mais baixos. Cada um desses índices representa um primeiro byte de um dos números ausentes (se houver menos de 4, é porque vários números ausentes compartilham o mesmo primeiro byte).

Para cada um desses índices, crie outra matriz de 256 contadores e faça uma segunda passagem no arquivo. Dessa vez, se o primeiro byte for um dos valores anteriores, aumente um contador em sua matriz com base no segundo byte. Quando terminar, procure novamente os contadores menores que 2 ^ 16 e você terá o segundo byte dos números ausentes, cada um correspondente ao primeiro byte.

Faça isso novamente para o terceiro byte (observe que você precisa de no máximo 4 matrizes em cada passagem, mesmo que cada byte possa ser seguido por até 4 bytes diferentes) e para o quarto byte, e você encontrou todos os números ausentes.

Complexidade do tempo - complexidade do O(n * log n)
espaço - constante !

Editar:

Na verdade, considerei o n=2^32parâmetro, mas o número de números ausentes k=4também é um parâmetro. Supondo que k<<nisso signifique a complexidade do espaço O(k).

Atualizar:

Apenas por diversão (e porque atualmente estou tentando aprender Rust), eu o implementei no Rust: https://gist.github.com/idanarye/90a925ebb2ea57de18f03f570f70ea1f . Optei por ter uma representação textual, já que on-one a executará com ~ 2 ^ 32 números ...

Idan Arye
fonte
Manter todos os números na memória (para várias passagens) requer 4 bytes * 2 ^ 32 de memória, o que está pressionando as coisas. Portanto, é mais provável que você faça todas as E / S quatro vezes. Mas a outra memória usada é extremamente pequena, um ótimo trabalho lá.
user949300
1
@ user949300 Estou assumindo esta solução lê a peça arquivo por peça em vez de carregar a coisa toda em memória de uma vez
Richard Tingle
"a maioria dos contadores deve estar em 2 ^ 24, mas 1 a 4 contadores devem ter valores mais baixos" - errado: pode ser 0, com todos os valores ausentes compartilhando o primeiro byte (também é possível o segundo e o terceiro). Próximo: quantos array você cria na segunda passagem? 256, 1 a 4 vezes 256, 256 vezes 256? E então no terceiro e quarto passe?
Bernhard Hiller
3
@BernhardHiller O arquivo contém todos os números possíveis no espaço de 32 bits, exceto por 4 números distintos. Como tal, todos os primeiros bytes ocorrerão, apenas 1 a 4 deles terão menos ocorrências.
Lasse V. Karlsen 17/10
@ LasseV.Karlsen obrigado, agora eu entendo o algoritmo.
Bernhard Hiller
6

Se fosse Java, você poderia usar um BitSet. Bem, dois deles, porque eles não conseguem conter todos os números de 32 bits. Código esquelético, talvez com erros:

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);
}

Em seguida, use BitSet.nextClearBit()para encontrar quem está faltando.

Nota adicionada muito mais tarde:

Observe que, com esse algoritmo, é bastante fácil executar a parte demorada em paralelo . Digamos que o arquivo original tenha sido dividido em quatro partes aproximadamente iguais. Aloque 4 pares de BitSets (2 GB, ainda gerenciáveis).

  1. Tenha quatro threads, em paralelo, cada processo um arquivo em seu próprio par de BitSets.
  2. Quando terminar, volte para um único encadeamento, ou Bitsets (horário trivial), e chame nextClearBit quatro vezes (também bastante trivial).

Eu esperaria que a E / S ainda fosse a etapa de limitação da taxa, mas se magicamente todos os números estivessem na memória, você poderia realmente acelerar as coisas.

user949300
fonte
3
@Idan Ayre. Esta solução requer pouco código, portanto, menos chance de erros de codificação. Eu sou bonita, é hora de O (n). Também não assume / requer várias passagens por um arquivo enorme, portanto, usa menos espaço do que um algoritmo que exige várias passagens. Por favor, elabore o que você quer dizer com "Oh querido".
user949300
2
Não manipula Integer.MIN_VALUEcorretamente. Você pode mascarar o bit de sinal em vez de negar para corrigi-lo.
CodesInChaos
1
Essa abordagem ingênua precisa de 2 ^ 32 bits = 4 Gib = 512 MiB para os bitsets, que é uma quantidade modesta de RAM, mesmo em um sistema de 32 bits.
CodesInChaos
Se o idioma de escolha não possuir bitsets integrados, emule-os usando uma matriz de bytes. Por exemplo, em C #:bool GetBit(byte[] byteArray, uint index) { var byteIndex = index >> 3; var bitInByte = index & 7; return (byteArray[byteIndex] >> bitInByte) & 1 != 0; }
CodesInChaos 17/10
1
@JoulinRouge (e JacquesB) Então, concordamos que isso é linear no tempo, usa RAM modesta (1/2 Gig) e leva apenas uma passagem de E / S. Funciona para mim.
user949300
5

Esta questão pode ser resolvida usando uma matriz de bits (verdadeiro / falso). Essa deve ser a estrutura mais eficiente para armazenar as respostas para todos os números usando o índice da matriz para armazenar se esse número específico foi encontrado.

C #

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;
        }
}

Em seguida, basta percorrer a matriz e, para os valores que ainda são falsos, eles não estão no arquivo.

Você poderia dividir o arquivo em pedaços menores, mas eu consegui alocar uma matriz de tamanho máximo int32 total (2147483647) no meu laptop de 16,0 GB com Windows 7 (64 bits).

Mesmo se não estivesse executando 64 bits, eu poderia alocar matrizes de bits menores. Eu pré-processava o arquivo criando um conjunto de arquivos menores, cada um com um intervalo de [0-64000] [64001-128000] etc. números que seriam adequados para os recursos ambientais disponíveis. Percorra o arquivo grande e escreva cada número no arquivo definido correspondente. Em seguida, processe cada arquivo menor. Levaria um pouco mais de tempo por causa da etapa de pré-processamento, mas isso contornaria as limitações de recursos se houvesse recursos limitados.

Jon Raynor
fonte
Isso não parece lidar com números negativos. (Ou entradas não assinadas com o bit mais alto definido, se essa for a entrada.) A memória para o conjunto de bits não deve ser um problema, mesmo na maioria dos sistemas de 32 bits.
user949300
@ user949300 - Correto. Não notei nenhum grande consumo de memória quando a matriz foi inicializada com todos os valores falsos. Seria necessário um BitArray secundário para os números negativos. Talvez bArrayNegative = new BitArrary (Int32.MaxValue). Quando o número foi lido, ele pode ser verificado como positivo ou negativo e, em seguida, colocado na matriz de bits apropriada. Obrigado pelos comentários.
Jon Raynor
2

Como se trata de uma pergunta de entrevista, eu mostraria ao entrevistador algum entendimento sobre as restrições. Então, o que significa "todos os números possíveis"? É realmente 0 ... 2 <(32-1) como todos imaginam? As arquiteturas comuns de 32 bits podem funcionar com muito mais do que números de 32 bits. É apenas uma questão de representação, obviamente.

Isso precisa ser resolvido em um sistema de 32 bits ou isso faz parte da restrição de números? Por exemplo, um sistema típico de 32 bits não poderá carregar o arquivo na RAM de uma só vez. Eu também mencionaria que um sistema de 32 bits geralmente não poderá ter um arquivo contendo todos os números devido à limitação do tamanho do arquivo. Bem, a menos que tenha alguma codificação inteligente, como "Todos os números, exceto os quatro", caso em que o problema é resolvido trivialmente.

Mas se você realmente deseja entender a pergunta como "Dado um arquivo com todos os números de 0 ... 2 ^ (32-1), exceto alguns, dê-me alguns que faltam" (e esse é um grande se !), Então existem muitas maneiras de resolvê-lo.

Trivial, mas inviável: para cada número possível, verifique o arquivo e verifique se ele está lá.

Com 512 MB de RAM e arquivo de passagem única: marque cada número (= bit definido nesse índice) lido no arquivo e depois passe a RAM uma vez e veja os que estão faltando.

Eiko
fonte
1
Algumas boas perguntas, mas se o sistema de 32 bits está representando entradas, flutuadores ou huzziwigs, ele ainda pode representar apenas 2 ^ 32 valores em 32 bits. Se a pergunta for "oh sim, permitimos comprimentos ultra-longos de 128 bits", a "restrição" da arquitetura de 32 bits na pergunta é deliberadamente enganosa. Ainda assim, é uma ótima pergunta para o entrevistador, porque muitas especificações são enganosas ou mal escritas. Sua solução atual é um BitSet como o meu.
user949300
@ user949300 Sim - e é impossível saber o que o entrevistador está procurando. Se a última pessoa que eles contrataram era do tipo "pilha de hackers antes de pensar", sua resposta deve ser diferente do que se fosse um "não tem absolutamente nenhuma idéia sobre arquitetura" ou "jogando o jogo de otimização". :) Já trabalhei com grandes conjuntos de bits antes (embora não em Java), então eles vêm à minha mente naturalmente. E pode ser adotado para diminuir a memória, se necessário (bucket). Os bitsets também resolvem o "problema de classificação" nos comentários acima em tempo linear com 512 MB de RAM.
Eiko
0

Uma abordagem fácil de lembrar e articular em uma entrevista seria usar o fato de que, se você olhar para todos os números em N bits, cada bit será definido exatamente na metade desses valores e não na outra metade .

Se você iterar sobre todos os valores no arquivo e manter 32 contagens dos valores no final, você terá 32 valores exatamente (2 ^ 32/2) ou ligeiramente inferiores a esse valor. A diferença que máximo (2 ^ 32/2) e o total fornece o total de bits definido em cada posição dos valores ausentes.

Depois de ter isso, você pode determinar todos os conjuntos possíveis de 4 valores que poderiam fornecer esses totais. Dado isso, é possível revisar os valores no arquivo novamente, verificando os valores que fazem parte dessas combinações. Quando você encontra uma, as combinações que contêm esse valor são eliminadas como possibilidades. Depois de ter apenas uma combinação possível, você responde.

Por exemplo, usando um nibble, você tem os seguintes valores:

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

O total de bits definidos em cada posição é:

7867

Subtraindo os de 8 (4 ^ 2/2), obtemos:

1021

O que significa que existem os seguintes conjuntos possíveis de 4 valores:

1000
0000
0011
0010

1010
0001
0010
0000

(me perdoe se eu perdi alguma, estou apenas fazendo isso de vista)

E então, olhando para os números originais novamente, encontramos 1010 imediatamente, o que significa que o primeiro conjunto foi a resposta.

JimmyJames
fonte
mas você tem que encontrar 4 números, não um
freedev
@ freedev Você está correto. É o que faz. Um conjunto de quatro números é quatro números ... em um conjunto.
21716 JimmyJames
Interessante, mas você encobre determine all the possible sets of 4 values that could give those totals. Realmente acho que essa é uma parte importante da solução que está faltando na sua resposta. Também pode afetar a complexidade do tempo e do espaço.
Allon Guralnek
@AllonGuralnek Você está certo. Passei um pouco de tempo trabalhando nisso e subestimei bastante quantos conjuntos de 4 números somariam o mesmo número no pior dos casos. Eu acho que essa é uma idéia que pode ser salva, mas é um pouco mais complicada do que eu expus aqui. Vou atualizar com detalhes mais tarde. Agradeço o feedback.
21416 JimmyJames
0

Supondo que o arquivo seja classificado por números crescentes:

Certifique-se de que o indeed contenha (2³²-4) números.
Agora, se o arquivo estiver completo (ou se os 4 números ausentes forem os últimos 4), a leitura de qualquer palavra no arquivo na posição N retornará o valor correspondente N.

Use uma pesquisa de dicotomia nas posições [0..2³²-4-1) para pesquisar e encontrar o primeiro número não esperado X1.
Depois de encontrar o primeiro número ausente, faça uma pesquisa de dicototomia novamente nas posições [X1 .. (2³²-4-1)] para encontrar o segundo ausente, X2: Desta vez, lendo uma palavra na posição N deve retornar o valor correspondente N-1 se não houvesse mais números ausentes (desde que você passou um número ausente).
Iterar da mesma forma para os dois números restantes. Na terceira iteração, a leitura da palavra na posição N deve retornar N-2 e, na quarta, deve retornar N-3.

Advertência: Eu não testei isso. Mas acho que deve funcionar. :)

Agora, na vida real, concordo com outras respostas: as primeiras perguntas seriam sobre o meio ambiente. Temos RAM disponível (quanto), é o arquivo em um dispositivo de armazenamento de acesso direto, é uma operação de tiro único (sem necessidade de otimização) ou crítica (cada contagem de ciclo), temos um utilitário de classificação externo disponível , etc.
Em seguida, encontre um compromisso aceitável para o contexto. Pelo menos isso mostra que você começa a analisar o problema antes de procurar um algoritmo.

filofel
fonte
-2

Como em todas as perguntas padrão, a solução é pesquisá-las no Google antes da entrevista.

Esta pergunta e variações têm uma resposta 'correta' muito definida, envolvendo XOR em todos os números. É suposto mostrar que você entende índices em bancos de dados ou algo assim. Portanto, zero ponto para qualquer resposta "pode ​​funcionar, mas não o que está escrito no papel", está imenso.

No lado positivo, há um conjunto finito dessas perguntas. Uma revisão de algumas horas fará com que você pareça um gênio. Lembre-se de fingir que está trabalhando na sua cabeça.

Editar. Ahh, parece que para 4 existe uma abordagem diferente da 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. Downvoters: Esta é uma solução de livro publicado O (n) para o problema exato indicado no OP.

Ewan
fonte
1
Notavelmente, este livro vinculado é sobre processamento de fluxo. Em particular, processamento de fluxo dentro de restrições. Dito isto, eu certamente iria acreditar que esta é a origem da questão do OP tem visto, uma vez que é de outra maneira muito trivial. Mais notavelmente, você realmente não respondeu à pergunta. Você receberá +1 de mim se puder convencer essa pergunta como "original" ou "pretendida" e explicar a solução ... mas isso não responde nada do jeito que está.
Svidgen
1
Esta resposta (em uma entrevista) mostra apenas que você leu o livro. Nada sobre suas habilidades ou processos de pensamento. E como você "pesquisa todas as perguntas padrão " no Google antes de uma entrevista? Existe alguma lista finita de "todas as perguntas já feitas em uma entrevista" que eu perdi?
user949300
1
@ewan também ressalta a dificuldade de contratar um bom candidato! Se os "bons" estão simplesmente bem preparados para as perguntas da entrevista ... Torna-se difícil contratar alguém que possa realmente resolver meus problemas de negócios?
svidgen
1
@ewan Para ser claro, eu estava tirando sarro da minha pontuação incorreta. ... De qualquer forma, lembre-se, também recebi um bom número de ofertas de emprego nos meus dias, mesmo sendo bastante ignorante das perguntas e respostas padrão como essa. E agora, como gerente de contratação, posso prometer que não quero respostas recitadas ... No entanto, entendo que alguns gerentes terão necessidades diferentes.
svidgen
1
@ Ewan Também devo esclarecer mais uma coisa, se meu tom não foi recebido como pretendido: você deve revisar sua resposta para realmente afirmar que o problema no livro vinculado ao livro é a "pergunta pretendida". E então responda a pergunta! ... Você, sem dúvida , teria o meu +1, e muitos outros, e a satisfação de ajudar o OP a fazê-lo.
svidgen