Funzione garantita per non restituire mai lo stesso valore due volte [chiuso]


23

Questa è una domanda che mi è stata posta durante un colloquio di lavoro, e non riesco a capire la risposta che stavano cercando, quindi spero che qualcuno qui possa avere qualche idea. L'obiettivo è scrivere una funzione che garantisca di non restituire mai lo stesso valore due volte. Supponiamo che questa funzione sia accessibile da più macchine contemporaneamente.

La mia idea era di assegnare a ogni macchina un ID univoco e passare quel valore nella funzione del generatore di valori univoci:

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

Ciò eviterebbe la ricaduta dalle condizioni di gara poiché anche se due o più processi leggono lo stesso valore per i, ogni valore di ritorno è etichettato come una combinazione unica di ID processo e ID macchina. Tuttavia, al mio intervistatore non è piaciuta questa risposta perché portare un'altra macchina online comporta l'assegnazione di un ID.

Quindi qualcuno può pensare a un altro modo per risolvere questo problema che non comporta la configurazione di ogni macchina per avere un ID univoco? Mi piacerebbe avere una risposta nel caso in cui questa domanda si presenti di nuovo. Grazie.


31
Garantito nel senso stretto della parola? Voglio dire, anche le guide ad un certo punto inizieranno a ripetersi. Potremmo non vivere più, ma garantisce ... E comunque, un ID processo è lungi dall'essere unico .
JensG,

7
@CodesInChaos - Questo è un presupposto abbastanza terribile, dato che in alcuni sistemi operativi è banale cambiare il tuo indirizzo mac.
Telastyn,

7
"Supponiamo che questa funzione sia accessibile da più macchine contemporaneamente" - onestamente, ciò potrebbe significare "il codice viene eseguito su ogni macchina in modo individuale, senza comunicazione tra le macchine", oppure "esiste un computer centrale / database centrale in cui la funzione è previsto per le altre macchine, disponibile in rete ". Dovresti iniziare a chiarire prima questo.
Doc Brown,

28
È stata una domanda trabocchetto? Ad esempio, una funzione contenente un ciclo infinito non restituirà mai lo stesso valore due volte.
Brendan,

8
Forse stavano cercando un programmatore che ponga domande su requisiti dubbi, piuttosto che fare ipotesi e correre con esso :)
theMayer

Risposte:


60

Non ti preoccupare, basta lanciare un semplice contatore (thread-safe) dietro un endpoint di comunicazione (WCF, servizio web, qualunque cosa):

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

Sì, finirà per traboccare. Sì, non gestisce i riavvii. Sì, non è casuale. Sì, qualcuno potrebbe eseguirlo su più server.

Questa è la cosa più semplice che soddisfa i requisiti pratici. Quindi lascia che siano loro a dare seguito a questi problemi (per essere sicuri che capiscano i limiti, pensano davvero che tu abbia bisogno di più di 2 ^ 64 ID), così puoi quindi chiedere quali compromessi vanno bene. Deve sopravvivere ai riavvii? Che dire del guasto del disco rigido? Che dire della guerra nucleare? Deve essere casuale? Quanto casuale?


7
Questa è una buona risposta, perché l'intervistatore non fa mai domande per ottenere una risposta diretta. Vogliono che tu dia una risposta in cui puoi giustificare le tue decisioni. Se capisci il dominio, quasi tutte le risposte saranno adatte se puoi giustificarlo.

7
Come dovrebbe funzionare se il codice viene eseguito su macchine diverse (quindi ovviamente in processi diversi)? Ogni processo avrà una copia diversa di x. E penso che senza una spiegazione del tipo di meccanismo di interblocco che hai in mente, questa risposta è piuttosto vaga.
Doc Brown,

7
@DocBrown "accessibile simultaneamente da più macchine" sembra implicare che più macchine accedano a una singola funzione su un singolo server. Altrimenti dovrebbe essere scritto "Più macchine eseguiranno una copia di questa funzione contemporaneamente"
Falco,

3
@LightnessRacesinOrbit: suppongo che questo sia pensato per essere C #, e la System.Threading.Interlockedclasse, che fornisce incrementi atomici. Ma potresti anche leggerlo come una specie di pseudo codice.
Doc Brown,

3
Se fossi la persona che chiedeva sarei molto insoddisfatto di questa proposta. Iniziare a implementare qualcosa senza nemmeno sapere quali sono i requisiti è una grande bandiera rossa. Mi aspetto che tu lo chieda.
JensG,

25

Se mi venisse posta questa domanda, e mi rendessero chiaro che deve essere unica tra i riavvii e tra macchine diverse, darei loro una funzione che chiama il meccanismo standard per creare un nuovo GUID, qualunque cosa accada la lingua utilizzata.


Il problema con i GUID v4 è che sono molto probabilmente unici, non garantiti come unici. In pratica non è un grosso problema, ma non soddisfa i requisiti se l'intervistatore li prende alla lettera.
CodesInChaos,

In particolare, se il meccanismo GUID standard non soddisfa i requisiti dell'intervistatore, prendere in considerazione le differenze nei requisiti tra l'intervistatore e un normale utente di GUID. Un intervistatore sensato che pone questo tipo di domanda ("come si fa a fare <qualcosa standard generalmente noto forse con una leggera variazione rispetto ai soliti requisiti>") dovrebbe aspettarsi tipi di risposta molto diversi dai candidati che conoscono lo stato dell'arte per GUID e candidati che stanno inventando qualcosa da zero.
Steve Jessop,

Questa è probabilmente la risposta più semplice, presupponendo requisiti flessibili.
the May

9
+1 perché questo è fondamentalmente il problema che le guide risolvono. Produrre una guida duplicata, indipendentemente dal suo formato, è la lotteria più difficile del pianeta. Apparentemente molte persone non hanno un senso per l'improbabilità esponenziale delle collisioni.
usr

3
Oh, e se offri la risposta "usa una funzione standard" a una di queste domande, aspettati una domanda di follow-up "e come viene implementata la funzione standard?". Alla quale potresti benissimo rispondere "Non lo so, ma lo guarderei sicuramente piuttosto che cercare di inventare qualcosa", che è una risposta completamente accurata che non riesce a mantenere la sospensione attesa dell'incredulità nelle condizioni dell'intervista, che avresti mai fatto qualcosa di importante senza prima averlo studiato ;-)
Steve Jessop,

22

L'intervistatore ha affermato che il metodo verrà chiamato contemporaneamente, non in parallelo; riporta semplicemente la data / ora al maggior numero di decimali possibile.

Perché tutti ci pensano troppo? Sarai morto molto tempo prima che finisca ogni finitezza e non hai la possibilità di una collisione.

Se sei preoccupato che ritorni nello stesso momento, aggiungi un ritardo per il minor tempo misurabile.

Se sei preoccupato di impostare un orologio per l'ora legale (sperimentando 1 volta due volte), aggiungi una costante all'ora la seconda volta che la provi.


12
O semplicemente restituire l'ora UTC indipendentemente dal fuso orario dei richiedenti. Poiché UTC non è localizzato, non sarà interessato dalle modifiche dell'ora legale.
Mauro,

1
System.currentTimeNanos () :-)
Falco,

1
A meno che tu non stia restituendo la data e l'ora in un formato leggibile dall'uomo, il tuo valore non dovrebbe comunque contenere al suo interno alcuna informazione sul fuso orario.
Corse di leggerezza con Monica,

12
Il minor tempo continuerà a produrre collisioni se chiamato abbastanza frequentemente / contemporaneamente. Produrrà anche collisioni dovute alla deriva della sincronizzazione dell'orologio, alla manipolazione dannosa dell'orologio e, se non stai attento, all'ora legale.
Telastyn,

1
Molto creativo, almeno. Fare affidamento su un orologio che verrà regolato di tanto in tanto non è ancora una grande idea, IMHO. L'offset non ti salverà dalle collisioni.
JensG,

15

In primo luogo, vorrai porre due domande all'intervistatore.


Domanda 1.

se l'intervistatore si aspetta che una o più "macchine centrali" vengano utilizzate per assegnare alcuni numeri univoci o blocchi di numeri univoci.


Domanda 2.

Se l'intervistatore si aspetta un meccanismo per il rilevamento delle collisioni o accetta invece il rischio calcolato di una minuscola possibilità di collisione senza rilevarle esplicitamente.

C'è anche l'approccio di difesa in profondità, in cui si incorpora una parte dell'ID utente nella casualità (quindi, non del tutto casuale). Si riduce quindi la possibilità che lo stesso utente incontri una collisione all'interno del contenuto creato da quello stesso utente.


C'è una domanda implicita 3, ...

Ma è uno che dovrai valutare te stesso senza chiedere, perché è estremamente scortese chiedere al tuo intervistatore.

Se l'intervistatore assume la conoscenza della probabilità, del rischio e di alcune semplici tecniche impiegate nei sistemi crittografici e di sicurezza delle informazioni.

Il primo tipo di conoscenza assicura che non stai cercando di convincere una persona non scientifica ad accettare un concetto scientifico che non accetteranno.

Il secondo tipo di conoscenza ti assicura di affrontare le preoccupazioni che si aggiungono alla mera probabilità. In altre parole, come difendersi dagli "aggressori" che vogliono rompere intenzionalmente lo schema di randomizzazione, manipolando le macchine oi loro host virtuali per forzare due macchine a generare lo stesso valore.


Perchè chiedere.

Il motivo è che se l'intervistatore se lo aspetta in un modo o nell'altro, provare a rispondere con l'approccio opposto non renderà mai felice l'intervistatore.

La ragione più profonda è che ad alcune persone non piace l'idea di dire, una 1.0e-20possibilità di fallire. (Cercherò di non suscitare argomenti filosofici o religiosi qui.)


Innanzitutto lo "spazio dei nomi" dei numeri casuali viene trasformato in una gerarchia, con un certo numero di bit allocati a una fonte di randomizzazione e l'altro numero di bit allocato in altri modi, ecc.

L'approccio centralizzato si basa su un'autorità centrale per assegnare in modo univoco il primo livello di bit. Quindi, le altre macchine possono riempire il resto dei bit.

Esistono diversi approcci decentralizzati:

  • Basta generare numeri casuali il meglio possibile e accettare la probabilità praticamente zero di fallire giustificata dai calcoli.
  • Usa mezzi crittografici per generare valori casuali dalla fonte deterministica, diciamo, valori incrementali.

Penso che questa sia la risposta migliore. Le altre sono soluzioni senza requisiti.
Jack Aidley,

Osservando la tua terza domanda - sembra che la competenza sia un presupposto sicuro, o almeno irrilevante. Se una società non ha fornito un intervistatore competente, ci saranno probabilmente maggiori difetti nel processo di selezione. In tal caso, apprezzerà le domande.
the May

1
Perché non è possibile rispondere alla "domanda 3" chiedendo qualcosa del tipo "Abbiamo bisogno di unicità veramente garantita o di una probabilità molto bassa di collisioni?" e "Quanto deve essere sicuro? Dobbiamo presumere che un aggressore cercherà di rompere il meccanismo? Di quali tipi di attacchi siamo preoccupati?" Le risposte a queste domande dovrebbero chiarire se il richiedente comprende questi problemi e cosa si aspettano.
jpmc26,

12

Quindi, tenendo presente che questa è una domanda di intervista e non uno scenario di vita reale, credo che l'approccio corretto (e probabilmente quello che l'intervistatore sta cercando) sia quello di porre una domanda di chiarimento, o di scrivere "Non può essere fatto "e andare avanti. Ecco perché.

Cosa chiede l'intervistatore:

Scrivi una funzione che è garantita per non restituire mai lo stesso valore due volte. Supponiamo che questa funzione sia accessibile da più macchine contemporaneamente.

Di cosa ha bisogno l'intervistatore:

Questo candidato valuta efficacemente i requisiti e cerca input aggiuntivi quando richiesto?

Non dare mai per scontato.

Quando un ingegnere riceve un requisito (tramite un SOW o una Specifica o un altro documento sui requisiti), alcuni sono evidenti e altri non sono del tutto chiari. Questo è un esempio perfetto di quest'ultimo. Come hanno dimostrato le risposte precedenti, non c'è modo di rispondere a questo requisito senza fare diverse ipotesi importanti (a) sulla natura della domanda o (b) sulla natura del sistema, poiché il requisito non può essere soddisfatto come scritto (è impossibile).

La maggior parte delle risposte fa un tentativo o un altro per risolvere il problema attraverso una serie di ipotesi. Uno specificamente consiglia di farlo in fretta e lasciare che il cliente se ne preoccupi se è sbagliato.

Questo è davvero un cattivo approccio. Come cliente, se do un requisito poco chiaro e l'ingegnere si spegne e mi costruisce una soluzione che non funziona, sarò sconvolto dal fatto che siano andati al lavoro e abbiano speso i miei soldi senza preoccuparmi di chiedermelo prima. Quel tipo di processo decisionale sprezzante dimostra mancanza di lavoro di squadra, incapacità di pensare in modo critico e scarso giudizio. Può portare a qualsiasi tipo di conseguenze negative, inclusa la perdita di vita in un sistema critico per la sicurezza.

Perché porre la domanda?

Il punto se questo esercizio è che è costoso e richiede tempo per costruire requisiti ambigui. Nel caso del PO, ti è stato assegnato un compito impossibile. La tua prima azione dovrebbe essere quella di chiedere chiarimenti: che cosa è richiesto? Quale grado di unicità è necessario? Cosa succede se un valore non è univoco? La risposta a queste domande potrebbe essere la differenza tra diverse settimane di tempo e alcuni minuti. Nel mondo reale, uno dei maggiori fattori di costo nei sistemi complessi (inclusi molti sistemi software) sono requisiti poco chiari e poco compresi. Ciò porta a bug costosi e dispendiosi in termini di tempo, riprogettazione, frustrazione di clienti e team e imbarazzante copertura mediatica se il progetto è abbastanza grande.

Cosa succede quando supponi?

Dato il mio background nel settore aerospaziale e la natura altamente visibile dei fallimenti aerospaziali, mi piace portare esempi da questo settore per illustrare punti importanti. Esaminiamo un paio di missioni fallite su Marte: Mars Climate Orbiter e Mars Polar Lander. Entrambe le missioni sono fallite a causa di problemi del software - perché gli ingegneri hanno formulato ipotesi non valide dovute, in parte, a requisiti poco chiari e poco comunicati.

Mars Climate Orbiter : questo caso viene in genere citato come ciò che accade quando la NASA tenta di convertire le unità inglesi in unità metriche. Tuttavia, questa è una rappresentazione eccessivamente semplicistica e scarsa di ciò che è realmente accaduto. È vero, c'era un problema di conversione, ma era dovuto a requisiti male comunicati in fase di progettazione e ad uno schema di verifica / validazione improprio. Inoltre, quando due diversi ingegneri hanno notato il problema perché era evidente dai dati della traiettoria di volo, non hanno sollevato il problema al livello corretto perché hanno assunto che si trattasse di un errore di trasmissione. Se il team operativo fosse stato informato del problema, ci sarebbe stato tempo sufficiente per correggerlo e salvare la missione. In questo caso, c'era una condizione logica impossibile che non era riconosciuta per quello che era, portando a costosi fallimenti della missione.

Mars Polar Lander- questo caso è un po 'meno noto, ma forse più imbarazzante a causa della sua vicinanza temporale al fallimento di Mars Climate Orbiter. In questa missione, il software controllava la discesa assistita dal propulsore del razzo sulla superficie marziana. In un punto a 40 metri sopra la superficie, le gambe del lander si schierarono in preparazione all'atterraggio. C'era anche un sensore sulle gambe che rilevava il movimento (per segnalare quando avevano avuto un impatto) per dire al software di spegnere il motore. La migliore ipotesi della NASA su ciò che è accaduto (perché ci sono più possibili guasti e dati incompleti) è che le vibrazioni casuali nelle gambe a causa del loro dispiegamento simultaneo e innescato in modo errato il meccanismo di spegnimento 40m sopra la superficie, con conseguente incidente e distruzione di $ 110 Veicolo spaziale M. Questa possibilità è stata sollevata nello sviluppo, ma non è mai stato affrontato. Alla fine, il team del software ha formulato ipotesi non valide sulla modalità di esecuzione di questo codice (una di queste ipotesi è che un segnale spurio sarebbe troppo breve per essere raccolto, nonostante i test mostrino il contrario), e tali ipotesi non sono mai state messe in discussione fino a dopo il fatto.

Considerazioni aggiuntive

Intervistare e valutare le persone è un affare complicato. Esistono diverse dimensioni di un candidato che un intervistatore potrebbe voler esplorare, ma una delle più importanti è la capacità di un individuo di pensare in modo critico. Per una serie di motivi, non ultimo il fatto che il pensiero critico sia mal definito, abbiamo difficoltà a valutare le capacità di pensiero critico.

Come istruttore di ingegneria, uno dei miei modi preferiti di valutare la capacità di uno studente di pensare in modo critico era quello di porre una domanda un po 'ambigua. Gli studenti più acuti prendevano in considerazione la premessa errata della domanda, la annotavano e o rispondevano data la premessa o rifiutavano di rispondere del tutto. In genere, vorrei porre una domanda simile alla seguente:

Prendi un disegno dalla tua pila di lavoro. Il disegno contiene una varietà di callout diversi, ma i punti più importanti indicano una superficie orizzontale e indicano "Perfettamente piatto". La superficie è larga 5 "per 16" e la parte è in alluminio. Come lavorerai la parte per creare questa funzione?

(A proposito, rimarrai scioccato dalla frequenza con cui una specifica così scarsa appare sul posto di lavoro.)

Mi aspetto che gli studenti riconoscano che non è possibile creare una funzione perfetta e che lo affermeranno nella loro risposta. In genere assegnerei un punto bonus se dicono che torneranno dal progettista e chiederanno chiarimenti prima di fare la parte. Se uno studente procede a dirmi come raggiungeranno la planarità .001 o qualche altro valore inventato, assegnerò zero punti. Questo mi aiuta a sottolineare ai miei studenti che devono pensare al quadro generale.

Linea di fondo

Se intervisto un ingegnere (o una professione simile), cerco qualcuno che possa pensare in modo critico e mettere in discussione ciò che gli è stato posto di fronte. Voglio qualcuno che ponga la domanda "Ha senso?" .

Non ha senso chiedere una parte perfettamente piatta, perché non esiste una cosa perfetta. Non ha senso chiedere una funzione che non restituisce mai un valore duplicato, perché è impossibile fornire una tale garanzia. Nella programmazione, spesso sentiamo la frase "immondizia, immondizia". Se ti viene consegnata la spazzatura per esigenze, è tua responsabilità etica fermarti e porre qualsiasi domanda ti aiuti a ottenere il vero intento. Se sto intervistando un candidato e gli do un requisito poco chiaro, mi aspetto delle domande di chiarimento.


5

Garantire l'unicità è difficile perché i computer non hanno variabili infinitamente grandi. Nessuna macchina del mondo reale può farlo.

Per come la vedo io ci sono due problemi qui, ed entrambi hanno soluzioni consolidate.

  • Concorrenza. Più macchine potrebbero aver bisogno di un valore contemporaneamente. Per fortuna, le moderne CPU hanno la concorrenza integrata e alcuni linguaggi offrono strutture a misura di sviluppatore per trarne vantaggio.
  • Unicità. Sebbene impossibile garantire l'unicità, possiamo avere variabili arbitrariamente grandi che possono contenere valori così grandi che un sistema del mondo reale avrebbe un momento molto difficile esaurendo tutti i valori unici

Ecco la mia soluzione in 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 è un tipo intero di dimensioni arbitrarie. Può crescere per contenere valori abbastanza grandi, anche se non infiniti. Il blocco garantisce la concorrenza, quindi lo stesso valore non può essere restituito due volte da due richieste simultanee gestite da thread separati.


Penso che il presupposto che il codice verrà utilizzato solo per meno di cinquecento anni sia un presupposto valido. Se restituisci semplicemente valori crescenti nella memoria a 64 bit, stai bene per un po '. A 1 chiamata per noi, in 584555 anni.
Mooing Duck,

1
Almeno in Java, ovvero 2 ^ 63 valori (quindi metà così lunghi). Ancora più a lungo della razza umana probabilmente esisterà data la nostra tendenza a ucciderci a vicenda. Indipendentemente da ciò, ho adottato un approccio più teorico. Realisticamente, 64 (o 63) bit dovrebbero essere sufficienti.

1
@Snowman: CHE COSA?!? La tua soluzione è valida solo per 250.000 anni?!?!? PROSSIMO CANDIDATO !!!!!! :-)
Bob Jarvis - Ripristina Monica il

0

Vorrei esporre la funzione tramite una porta sul server; per chiamare la funzione, la macchina richiedente richiede una connessione e gli viene concessa una, mentre allo stesso tempo viene assegnato un codice identificativo (numero progressivo per semplicità). Ogni volta che un messaggio viene inviato alla porta che richiede il valore univoco, il valore viene generato concatenando l'hash MD5 della data e ora correnti con l'hash MD5 del codice identificativo.

Se desiderano una soluzione più antiproiettile, dovrebbero specificare i loro requisiti effettivi piuttosto che essere tutti vaghi sulle cose.


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

In questo modo possiamo assicurarci che il valore di ritorno sia diverso anche se ci sono riavvii o anche se chiamati simultaneamente da macchine diverse.


I programmatori riguardano domande e risposte concettuali che dovrebbero spiegare le cose. Lanciare dump di codice anziché una spiegazione è come copiare il codice dall'IDE alla lavagna: può sembrare familiare e persino a volte comprensibile, ma sembra strano ... solo strano. La lavagna non ha compilatore
moscerino

Grazie moscerino per averlo sottolineato, si occuperà di spiegare la soluzione dalla prossima volta
techExplorer
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.