Função garantida para nunca retornar o mesmo valor duas vezes [fechado]

23

Essa é uma pergunta que me foi feita em uma entrevista de emprego e não consigo descobrir a resposta que eles estavam procurando, então espero que alguém aqui possa ter algumas idéias. O objetivo é escrever uma função que é garantida para nunca retornar o mesmo valor duas vezes. Suponha que essa função será acessada por várias máquinas simultaneamente.

Minha ideia era atribuir a cada máquina um ID exclusivo e passar esse valor para a função exclusiva de gerador de valor:

var i = 0;
function uniq(process_id, machine_id) {
   return (i += 1).toString() + machine_id + "-" + process_id;
}

Isso evitaria as consequências das condições de corrida, pois, mesmo que dois ou mais processos leiam o mesmo valor i, cada valor de retorno será marcado como uma combinação exclusiva de identificação do processo e identificação da máquina. No entanto, meu entrevistador não gostou desta resposta, porque colocar outra máquina on-line envolve atribuir um ID a ela.

Então, alguém pode pensar em outra maneira de resolver isso que não envolve a configuração de cada máquina para ter um ID exclusivo? Eu gostaria de ter uma resposta caso essa pergunta surja novamente. Obrigado.

Jay
fonte
31
Garantido no sentido estrito da palavra? Quero dizer, até Guids começará a se repetir em algum momento. Podemos não viver mais, mas garantimos. E, a propósito, um ID de processo está longe de ser único .
JensG
7
@CodesInChaos - Essa é uma suposição bastante terrível, já que é trivial em alguns sistemas operacionais alterar o endereço do seu Mac.
Telastyn
7
"Suponha que essa função seja acessada por várias máquinas simultaneamente" - honestamente, isso pode significar "o código é executado em cada máquina individualmente, sem comunicação entre as máquinas" ou "existe um banco de dados central da máquina / central onde a função é fornecido para as outras máquinas, disponíveis na rede ". Você deve começar a esclarecer isso primeiro.
Doc Brown
28
Foi uma pergunta enganadora? Por exemplo, uma função contendo um ciclo infinito nunca voltar o mesmo valor duas vezes ..
Brendan
8
Talvez eles estavam procurando um programador que faz perguntas sobre requisitos duvidosos, ao invés de fazer suposições e correr com ele :)
theMayer

Respostas:

60

Não seja chique, basta jogar um contador simples (thread-safe) atrás de algum ponto de extremidade de comunicação (WCF, serviço da web, o que for):

   long x = long.MinValue;
   public long ID(){
       return Interlocked.Increment(ref x);
   }

Sim, acabará por transbordar. Sim, ele não suporta reinicializações. Sim, não é aleatório. Sim, alguém poderia executar isso em vários servidores.

Essa é a coisa mais simples que satisfaz os requisitos práticos. Em seguida, deixe- os serem os que acompanham esses problemas (para garantir que eles entendam as limitações, eles realmente acham que você precisa de mais de 2 ^ 64 IDs), para que você possa perguntar sobre quais compensações estão bem. Ele precisa sobreviver a reinicializações? E a falha no disco rígido? E a guerra nuclear? Precisa ser aleatório? Quão aleatório?

Telastyn
fonte
7
Esta é uma boa resposta, porque o entrevistador nunca faz perguntas para obter uma resposta direta. Eles querem que você dê uma resposta onde possa justificar suas decisões. Se você entende o domínio, quase qualquer resposta será adequada se você puder justificá-lo.
7
Como isso deve funcionar se o código for executado em máquinas diferentes (tão obviamente em processos diferentes)? Cada processo terá uma cópia diferente de x. E acho que sem uma explicação sobre o tipo de mecanismo de bloqueio que você tem em mente, essa resposta é bastante vaga.
Doc Brown
7
@DocBrown "acessado por várias máquinas simultaneamente" parece implicar que várias máquinas acessam uma única função em um único servidor. Caso contrário, deve ser redigido "Múltiplas máquinas executarão uma cópia dessa função ao mesmo tempo"
Falco
3
@LightnessRacesinOrbit: Eu acho que isso deve ser C #, e a System.Threading.Interlockedclasse, que fornece incrementos atômicos. Mas você também pode ler isso como algum tipo de pseudo-código.
Doc Brown
3
Se eu fosse a pessoa que perguntava, ficaria muito infeliz com esta proposta. Começar a implementar algo sem saber quais são os requisitos é uma grande bandeira vermelha. Eu esperaria que você perguntasse.
JensG
25

Se eu fizesse essa pergunta e eles deixassem claro que ela deve ser única nas reinicializações e nas máquinas diferentes, eu daria a eles uma função que chama o mecanismo padrão para criar um novo GUID, seja lá o que acontecer o idioma que está sendo usado.

Mason Wheeler
fonte
O problema com os GUIDs da v4 é que eles provavelmente são únicos e não garantidos. Não é um grande problema na prática, mas não satisfaz os requisitos se o entrevistador os considerar literalmente.
CodesInChaos
Em particular, se o mecanismo GUID padrão não atender aos requisitos do entrevistador, elimine as diferenças nos requisitos entre o entrevistador e um usuário comum dos GUIDs. Um entrevistador sensato que faz esse tipo de pergunta ("como você faz alguma coisa padrão geralmente conhecida, talvez com uma ligeira variação dos requisitos habituais>") deve esperar respostas muito diferentes dos candidatos que conhecem o estado da arte para GUIDs e candidatos que estão inventando algo do zero.
Steve Jessop
Esta é provavelmente a resposta mais simples, assumindo requisitos flexíveis.
theMayer
9
+1 porque esse é basicamente o problema que os guias resolvem. Produzir um Guid duplicado, independentemente do formato, é a loteria mais difícil do planeta. Aparentemente, muitas pessoas não têm noção da improvabilidade exponencial de colisões.
usr
3
Ah, e se você oferecer a resposta "usar uma função padrão" para uma dessas perguntas, espere uma pergunta de acompanhamento "e como a função padrão é implementada?". Para o qual você pode muito bem responder "Eu não sei, mas eu definitivamente procuraria em vez de tentar inventar alguma coisa", que é uma resposta completamente precisa que falha completamente em manter a suspensão esperada da descrença nas condições da entrevista, que você nunca fazer qualquer coisa importante sem pesquisar primeiro ;-)
Steve Jessop
22

O entrevistador disse que o método será chamado simultaneamente, não em paralelo; basta retornar a data / hora para o máximo de casas decimais possível.

Por que todo mundo está pensando demais nisso? Você ficará morto muito tempo antes que qualquer finitude seja gasta e não terá chance de colisão.

Se você estiver preocupado com o retorno da mesma hora, adicione um atraso pelo menor tempo mensurável.

Se você estiver preocupado com a possibilidade de adiar o relógio para o horário de verão (experimentando uma vez duas vezes), adicione uma constante à hora na segunda vez em que o experimentar.

Brian
fonte
12
Ou apenas retorne a hora UTC, independentemente do fuso horário dos solicitantes. Como o UTC não está localizado, não será afetado pelas alterações no horário de verão.
Mauro
1
System.currentTimeNanos () :-)
Falco
1
A menos que você retorne a data e a hora em um formato legível por humanos, seu valor não deverá conter nenhuma informação de fuso horário.
Lightness Races com Monica
12
A menor quantidade de tempo ainda produzirá colisões se for chamada com frequência / simultaneamente. Também produzirá colisões devido ao desvio da sincronização do relógio, manipulação maliciosa do relógio e, se você não for cuidadoso - horário de verão.
Telastyn
1
Muito criativo, pelo menos. Contar com um relógio que será ajustado de vez em quando ainda não é uma ótima idéia, IMHO. O deslocamento não o salvará de colisões.
JensG
15

Primeiro, você deve fazer duas perguntas ao entrevistador.


Questão 1.

se o entrevistador espera que uma ou mais "máquinas centrais" sejam usadas para atribuir alguns números únicos ou blocos de números únicos.


Questão 2.

Se o entrevistador espera um mecanismo para a detecção de colisão, ou aceita o risco calculado de uma chance minúscula de colisão sem detectá-lo explicitamente.

Existe também a abordagem de defesa em profundidade, na qual se incorpora parte do ID do usuário à aleatoriedade (portanto, não inteiramente aleatória). Portanto, a chance de o mesmo usuário encontrar uma colisão no conteúdo criado por esse mesmo usuário é reduzida.


Existe uma pergunta implícita 3, ...

Mas é preciso avaliar a si mesmo sem perguntar, porque é extremamente indelicado perguntar ao seu entrevistador.

Se o entrevistador assume o conhecimento de probabilidade, risco e algumas técnicas simples empregadas em sistemas de criptografia e segurança da informação.

O primeiro tipo de conhecimento garante que você não esteja tentando convencer uma pessoa não científica a aceitar um conceito científico que ela não aceitará.

O segundo tipo de conhecimento garante que você lide com preocupações que são adicionais à mera probabilidade. Em outras palavras, como se defender de "agressores" que desejam intencionalmente interromper seu esquema de randomização, manipulando a (s) máquina (s) ou seus hosts virtuais para forçar duas máquinas a gerar o mesmo valor.


Porque perguntar.

O motivo é que, se o entrevistador espera isso de uma maneira ou de outra, tentar responder com a abordagem oposta nunca fará o entrevistador feliz.

A razão mais profunda é que algumas pessoas não gostam da idéia de dizer, uma 1.0e-20chance de falhar. (Tentarei não levantar argumentos filosóficos ou religiosos aqui.)


Primeiro, o "espaço para nome" dos números aleatórios é transformado em uma hierarquia, com um certo número de bits alocado para uma fonte de randomização e o outro número de bits alocado para outras formas, etc.

A abordagem centralizada conta com alguma autoridade central para atribuir exclusivamente o primeiro nível de bits. Em seguida, as outras máquinas podem preencher o restante dos bits.

Existem várias abordagens descentralizadas:

  • Apenas gere números aleatórios da melhor maneira possível e aceite a chance praticamente zero de falha justificada pelos cálculos.
  • Use meios criptográficos para gerar valores aleatórios a partir de fontes determinísticas, digamos, valores incrementais.
rwong
fonte
Eu acho que essa é a melhor resposta. Os outros são soluções sem requisitos.
Jack Aidley
Observando sua terceira pergunta - parece que competência é uma suposição segura, ou pelo menos irrelevante. Se uma empresa não forneceu um entrevistador competente, provavelmente haverá falhas maiores no processo de seleção. Se eles fizeram, então ele / ela irá apreciar as perguntas.
theMayer
1
Por que a "questão 3" não pôde ser abordada perguntando algo como "Precisamos de uma exclusividade verdadeiramente garantida ou apenas de uma probabilidade muito, muito baixa de colisões?" e "Quão seguro isso precisa ser? Precisamos assumir que um invasor estará tentando quebrar o mecanismo? Com ​​que tipos de ataques estamos preocupados?" As respostas a essas perguntas devem esclarecer se o solicitante entende esses problemas e o que espera.
jpmc26
12

Portanto, tendo em mente que essa é uma pergunta de entrevista e não um cenário real da vida real, acredito que a abordagem correta (e provavelmente o que o entrevistador está procurando) é fazer uma pergunta esclarecedora ou escrever "Não pode ser feito "e seguir em frente. Aqui está o porquê.

O que o entrevistador pergunta:

Escreva uma função que é garantida para nunca retornar o mesmo valor duas vezes. Suponha que essa função será acessada por várias máquinas simultaneamente.

O que o entrevistador precisa:

Esse candidato avalia efetivamente os requisitos e busca informações adicionais quando necessário?

Nunca assuma.

Quando um engenheiro recebe um requisito (por meio de uma SOW ou Especificação ou algum outro documento de requisitos), alguns são evidentes e outros são totalmente incertos. Este é um exemplo perfeito deste último. Como as respostas anteriores mostraram, não há como responder a esse requisito sem fazer várias suposições principais (a) quanto à natureza da pergunta ou (b) quanto à natureza do sistema, porque o requisito não pode ser atendido. como está escrito (é impossível).

A maioria das respostas faz uma tentativa ou outra de resolver o problema por meio de uma série de suposições. Recomenda-se especificamente fazê-lo rapidamente e deixar o cliente se preocupar se estiver errado.

Esta é realmente uma péssima abordagem. Como cliente, se eu der um requisito claro, e o engenheiro sair e me criar uma solução que não funcione, ficarei chateado que eles tenham ido trabalhar e gastaram meu dinheiro sem se preocupar em me perguntar primeiro. Esse tipo de tomada de decisão descuidada demonstra falta de trabalho em equipe, incapacidade de pensar criticamente e mau julgamento. Pode levar a qualquer tipo de conseqüências negativas, incluindo perda de vida em um sistema crítico de segurança.

Por que fazer a pergunta?

O ponto se este exercício é que é caro e demorado criar requisitos ambíguos. No caso do OP, você recebeu uma tarefa impossível. Sua primeira ação deve ser pedir esclarecimentos - o que é necessário? Que grau de exclusividade é necessário? O que acontece se um valor não for exclusivo? A resposta para essas perguntas pode ser a diferença entre várias semanas e alguns minutos. No mundo real, um dos maiores fatores de custo em sistemas complexos (incluindo muitos sistemas de software) são requisitos pouco claros e pouco compreendidos. Isso leva a erros caros e demorados, redesenhamentos, frustração de clientes e equipes e cobertura da mídia embaraçosa se o projeto for grande o suficiente.

O que acontece quando você assume?

Dada a minha experiência na indústria aeroespacial e devido à natureza altamente visível das falhas aeroespaciais, gosto de trazer exemplos desse domínio para ilustrar pontos importantes. Vamos examinar um par de missões fracassadas em Marte - o Mars Climate Orbiter e o Mars Polar Lander. Ambas as missões falharam devido a problemas de software - porque os engenheiros fizeram suposições inválidas devido, em parte, a requisitos pouco claros e pouco comunicados.

Mars Climate Orbiter - este caso é tipicamente citado como o que acontece quando a NASA tenta converter inglês em unidades métricas. No entanto, essa é uma representação excessivamente simplista e pobre do que realmente aconteceu. É verdade que houve um problema de conversão, mas isso ocorreu devido a requisitos pouco comunicados na fase de design e a um esquema de verificação / validação incorreto. Além disso, quando dois engenheiros diferentes perceberam o problema porque era óbvio a partir dos dados da trajetória de vôo, eles não levaram o problema ao nível adequado porque supuseram que se tratava de um erro de transmissão. Se a equipe de operações da missão estivesse ciente do problema, havia tempo suficiente para corrigi-lo e salvar a missão. Nesse caso, havia uma condição lógica impossível que não era reconhecida pelo que era, levando a dispendiosas falhas na missão.

Mars Polar Lander- este caso é um pouco menos conhecido, mas possivelmente mais embaraçoso devido à sua proximidade temporal à falha do Mars Climate Orbiter. Nesta missão, o software controlava a descida do foguete assistida pelo propulsor na superfície marciana. Em um ponto a 40 metros acima da superfície, as pernas do veículo pousaram em preparação para o pouso. Havia também um sensor nas pernas que detectava movimento (para sinalizar quando foram afetados) para dizer ao software para desligar o motor. O melhor palpite da NASA sobre o que aconteceu (porque existem várias falhas possíveis e dados incompletos) é que vibrações aleatórias nas pernas devido à sua implantação simultânea e indevidamente acionaram o mecanismo de desligamento 40m acima da superfície, resultando no acidente e na destruição dos US $ 110 Nave espacial M. Essa possibilidade foi levantada no desenvolvimento, mas nunca foi abordado. Por fim, a equipe de software fez suposições inválidas sobre como esse código precisava ser executado (uma dessas suposições é que um sinal espúrio teria vida curta demais para ser captado, apesar dos testes mostrarem o contrário), e essas suposições nunca foram questionadas até depois o fato.

Considerações adicionais

Entrevistar e avaliar pessoas é um negócio complicado. Existem várias dimensões de um candidato que um entrevistador pode querer explorar, mas uma das mais importantes é a capacidade de um indivíduo de pensar criticamente. Por várias razões, entre as quais o pensamento crítico é mal definido, temos dificuldade em avaliar as habilidades de pensamento crítico.

Como instrutor de engenharia, uma das minhas maneiras favoritas de avaliar a capacidade de um aluno de pensar criticamente era fazer uma pergunta um tanto ambígua. Os alunos mais aguçados entendiam a premissa defeituosa da pergunta, observavam-na e respondiam, dada a premissa, ou se recusavam a responder por completo. Normalmente, eu faria uma pergunta semelhante à seguinte:

Você escolhe um desenho da sua pilha de trabalho. O desenho contém uma variedade de textos explicativos diferentes, mas os pontos mais importantes para uma superfície horizontal e diz "Perfeitamente plano". A superfície tem 5 "de largura por 16" de comprimento e a peça é feita de alumínio. Como você usinará a peça para criar esse recurso?

(A propósito, você ficaria chocado com a frequência com que uma especificação tão ruim aparece no local de trabalho.)

Espero que os alunos reconheçam que não é possível criar um recurso perfeito e que eles declarem isso em sua resposta. Normalmente, eu atribuiria um ponto de bônus se eles disserem que voltarão ao designer e pedirão esclarecimentos antes de fazer a peça. Se um aluno começar a me dizer como alcançará a planaridade 0,001 ou algum outro valor compensado, concedo zero pontos. Isso me ajuda a mostrar aos meus alunos que eles precisam pensar no quadro geral.

Bottom Line

Se estou entrevistando um engenheiro (ou profissão similar), procuro alguém que possa pensar criticamente e questionar o que foi colocado na sua frente. Quero alguém que faça a pergunta "Isso faz sentido?" .

Não faz sentido pedir uma peça perfeitamente plana, porque não existe algo perfeito. Não faz sentido solicitar uma função que nunca retorne um valor duplicado, porque é impossível fazer essa garantia. Na programação, geralmente ouvimos a frase "lixo dentro, lixo fora". Se você recebe lixo para requisitos, é sua responsabilidade ética parar e fazer qualquer pergunta que o ajude a obter a verdadeira intenção. Se eu estiver entrevistando um candidato e lhes der um requisito incerto, esperarei perguntas de esclarecimento.

theMayer
fonte
5

Garantir a exclusividade é difícil, porque os computadores não possuem variáveis ​​infinitamente grandes. Nenhuma máquina Turing do mundo real pode.

A meu ver, existem dois problemas aqui, e ambos têm soluções bem estabelecidas.

  • Concorrência. Várias máquinas podem precisar de um valor ao mesmo tempo. Felizmente, as CPUs modernas possuem simultaneidade embutida e alguns idiomas fornecem recursos amigáveis ​​ao desenvolvedor para tirar proveito disso.
  • Singularidade. Enquanto impossível singularidade garantia, podemos ter arbitrariamente grandes variáveis que podem conter valores tão grande que um sistema do mundo real teria um muito tempo difícil de esgotar todos os valores exclusivos

Aqui está a minha solução em Java:

public class Foo {
  private static BigInteger value = BigInteger.ZERO;
  private static final Lock lock = new ReentrantLock();

  public static BigInteger nextValue() {
    try {
      lock.lock();
      value = value.add(BigInteger.ONE);
      return value;
    }
    finally {
      lock.unlock();
    }
  }
}

BigInteger é um tipo inteiro de tamanho arbitrário. Pode crescer para manter valores muito grandes, mesmo que não sejam infinitos. O bloqueio garante simultaneidade, portanto, o mesmo valor não pode ser retornado duas vezes por duas solicitações simultâneas atendidas por threads separados.

ChaosPandion
fonte
Eu acho que a suposição de que o código será usado apenas por menos de quinhentos anos é uma suposição válida. Se você simplesmente retornar valores crescentes no armazenamento de 64 bits, ficará bem por um tempo. Em uma chamada por nós, em 584555 anos.
Mooing Duck
1
Pelo menos em Java, são 2 ^ 63 valores (portanto, metade desse tempo). Muito mais tempo do que a raça humana provavelmente existirá, dada a nossa tendência de nos matar. Independentemente disso, adotei uma abordagem mais teórica. Realisticamente, 64 (ou 63) bits devem ser suficientes.
1
@Snowman: O QUE?!? Sua solução é válida apenas por 250 mil anos?!?!? PRÓXIMO CANDIDATO !!!!!! :-)
Bob Jarvis - Reinstala Monica 25/11
0

Eu exporia a função através de uma porta no servidor; Para chamar a função, a máquina solicitante solicita uma conexão e recebe uma, enquanto ao mesmo tempo recebe um código de identificação (número sequencial por simplicidade). Sempre que uma mensagem é enviada para a porta solicitando o valor exclusivo, o valor é gerado concatenando o hash MD5 da data e hora atuais com o hash MD5 do código de identificação.

Se eles querem uma solução mais à prova de balas, teriam que especificar seus requisitos reais, em vez de serem todos vagos sobre as coisas.

thespratty
fonte
-1
string uniq(string machine_id) 
{
   static long u = long.MinValue;
   Interlocked.Increment(ref u);

   //Time stamp with millisecond precison
   string timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff",
                                            CultureInfo.InvariantCulture);

   return machine_id + "-" + timestamp + "-" + u;
}

Da maneira acima, podemos garantir que o valor de retorno seja diferente, mesmo se houver reinicialização ou mesmo se for chamado simultaneamente de máquinas diferentes.

techExplorer
fonte
Programadores trata de perguntas conceituais e espera-se que as respostas expliquem as coisas. Jogar dumps de código em vez de explicação é como copiar código do IDE para o quadro branco: pode parecer familiar e até às vezes compreensível, mas parece estranho ... apenas estranho. Whiteboard não tem compilador
mosquito
Obrigado gnat por apontá-lo, terá o cuidado de explicar a solução da próxima vez
techExplorer