È accettabile affidarsi che gli ints casuali siano unici?


42

Ho implementato un protocollo di rete e ho bisogno che i pacchetti abbiano identificatori univoci. Finora, ho appena generato numeri interi casuali a 32 bit e presumo che sia astronomicamente improbabile che ci sia una collisione durante la durata di un programma / connessione. Questo è generalmente considerato una pratica accettabile nel codice di produzione o si dovrebbe escogitare un sistema più complesso per prevenire le collisioni?


47
Perché usare un numero intero sequenziale non lo taglierà?
whatsisname

20
Perché non usi semplicemente un int incrementale? I GUID , progettati per avere le proprietà di unicità che descrivi, hanno una dimensione di 128 bit, non di 32.
Robert Harvey,

21
In alternativa, assegnare un numero di canale a ciascun computer collegato e utilizzare un ID sequenza progressiva. I due numeri combinati (con il numero del canale che occupa i bit di ordine superiore) diventano il tuo nuovo ID univoco.
Robert Harvey,

27
Se il tuo "generatore di numeri casuali" garantisce che un determinato numero non verrà ripetuto fino a quando non viene generato ogni altro numero, è un generatore di numeri casuali molto scarso! Secondo la stessa logica, l'unica possibile sequenza "casuale" di lanci di monete sarebbe HTHTHTHTHT ....
alephzero,

17
"Richiedo che i pacchetti abbiano identificatori univoci" Qual è la conseguenza della violazione di questo requisito? Se hai bisogno di identificatori univoci, nella lettura più rigorosa della parola, devi disporre di un sistema centralizzato che metta in atto identificatori (come il modo in cui i MAC sono assegnati alle singole società di schede di rete). Molto probabilmente hai una definizione più morbida di "richiedere". Comprendere quel livello di morbidezza cambierà radicalmente le risposte che ricevi.
Cort Ammon,

Risposte:


142

Attenti al paradosso del compleanno .

Supponiamo di generare una sequenza di valori casuali (uniformemente, indipendentemente) da un insieme di dimensioni N (N = 2 ^ 32 nel tuo caso).

Quindi, la regola empirica per il paradosso del compleanno afferma che una volta generato circa i valori sqrt (N), esiste almeno il 50% di probabilità che si sia verificata una collisione, ovvero che ci siano almeno due valori identici nel sequenza generata.

Per N = 2 ^ 32, sqrt (N) = 2 ^ 16 = 65536. Quindi, dopo aver generato circa 65k identificatori, è più probabile che due di loro si scontrino che no! Se si genera un identificatore al secondo, ciò avverrebbe in meno di un giorno; inutile dirlo, molti protocolli di rete funzionano molto più velocemente di così.


11
+1. Nel mio ultimo lavoro, uno dei nostri partner ha effettivamente utilizzato questo approccio per generare identificatori casuali (non per pacchetti di rete, ma per un oggetto di business condiviso creato in ultima analisi dai clienti finali). Quando ho interrogato i dati con un occhio verso questo, ho scoperto che in media c'erano due o tre coppie di duplicati ogni giorno. (Fortunatamente, questo ha rotto le cose solo se i duplicati sono stati creati entro quattro ore l'uno dall'altro, il che è accaduto un po 'meno spesso. Ma comunque.)
ruakh

6
(fai clic qui per visualizzare la matematica) Per quel che vale, l'approssimazione $ \ sqrt {N} $ è accurata fino a un fattore costante; per $ N = 2 ^ {32} $, la soglia effettiva è 77164, poiché questo è il valore più piccolo di $ n $ tale che $ \ prod_ {k = 1} ^ {n-1} (1 - k / N) <1 / 2. $
wchargin

4
@wchargin: non c'è davvero nulla di magico nella probabilità di colpire 0,5; ciò che è notevole è che la probabilità sta aumentando relativamente velocemente con l'aumento di N. Se gli identificatori a 32 bit avrebbero una leggera ma non banale possibilità di una collisione casuale, un identificatore a 40 bit non ne avrebbe quasi nessuno.
supercat

3
@supercat: è tutto vero. Ho appena immaginato che se uno fornisce una tale costante, si potrebbe anche dare un valore preciso :-)
wchargin

2
@wchargin: preferisco pensare in termini di dove uno deve iniziare a preoccuparsi dei duplicati. Se si va molto al di sotto di sqrt (N) le probabilità di collisioni diminuiscono rapidamente, al punto che si può tranquillamente affermare che non accadranno a meno che non vi sia un grave difetto nel generatore casuale.
supercat

12

È ampiamente accettabile fare affidamento sul fatto che numeri casuali siano univoci se quei numeri hanno abbastanza bit. Esistono protocolli crittografici in cui la ripetizione di un numero casuale interromperà l'intera sicurezza. E fintanto che non ci sono gravi vulnerabilità nel generatore di numeri casuali in uso, questo non è stato un problema.

Uno degli algoritmi per la generazione di UUID genererà effettivamente un ID costituito da 122 bit casuali e presuppone che sarà univoco. E due degli altri algoritmi si basano sul fatto che un valore di hash troncato a 122 bit sia unico, il che ha all'incirca lo stesso rischio di collisioni.

Quindi ci sono standard che fanno affidamento sul fatto che 122 bit sono sufficienti per rendere univoco un ID casuale, ma 32 bit non sono assolutamente sufficienti. Con ID a 32 bit sono necessari solo circa 2¹⁶ ID prima che il rischio di una collisione raggiunga il 50% perché con 2¹⁶ ID ci saranno quasi 2³¹ coppie ciascuna delle quali potrebbe essere una collisione.

Anche 122 bit è inferiore a quello che consiglierei in qualsiasi nuovo design. Se per te è importante seguire una certa standardizzazione, usa gli UUID. Altrimenti usa qualcosa di più grande di 122 bit.

La funzione hash SHA1 con un'uscita di 160 bit non è più considerata sicura, in parte perché 160 bit non sono sufficienti per garantire l'univocità delle uscite. Le moderne funzioni hash hanno output da 224 a 512 bit. Gli ID generati casualmente dovrebbero puntare alle stesse dimensioni per garantire unicità con un buon margine di sicurezza.


12
SHA-1 è considerato insicuro perché ci sono attacchi specifici (cioè non casuali) contro l'algoritmo stesso che possono trovare collisioni più velocemente della forza bruta, non perché c'è un'alta probabilità di una collisione casuale. Una stima approssimativa afferma che con 122 bit e un tasso di generazione di 1 miliardo (10 ^ 9) ID al secondo, occorrerebbero oltre 73 anni prima di raggiungere una probabilità del 50% di una collisione.
8

sqrt(2^122)= 2,3 quadrilioni di quadrilioni di UUID
noɥʇʎԀʎzɐɹƆ

2
@ 8bittree La rete bitcoin calcola 2 hash SHA2 ogni 10 minuti. Se fossero stati gli hash SHA1, ci sarebbe voluta solo una settimana per produrre una collisione. Se gli UUID fossero prodotti alla stessa velocità con cui il bitcoin calcola gli hash, occorrerebbero meno di 2 secondi per produrre una collisione.
Kasperd,

Bitcoin sta cercando di trovare collisioni, è immensamente popolare e ha hardware dedicato progettato specificamente per la ricerca di hash. Ora, sicuramente, se l'OP sta pianificando di creare una criptovaluta molto popolare o qualcosa di simile, potrebbero aver bisogno di centinaia o migliaia di bit per ID. Ma supporre immediatamente che questi siano i requisiti potrebbe incoraggiare molto più lavoro del necessario se una libreria UUID standard è sufficiente.
8

@ 8bittree Se l'utilizzo di librerie standard è un vantaggio, allora scegli UUID. Ma estrarre alcuni byte casuali da urandomnon è più un lavoro che usare una libreria UUID. Ho appena implementato entrambi in Python per il confronto, e ogni metodo era esattamente 25 caratteri di codice sorgente.
Kasperd,

3

Definirei questa cattiva pratica. Il numero casuale genera semplicemente non crea numeri univoci, ma solo numeri casuali. È probabile che una distribuzione casuale includa alcuni duplicati. È possibile rendere questa circostanza accettabilmente improbabile aggiungendo un elemento di tempo. Se si ottiene l'ora corrente dall'orologio di sistema in millisecondi. Qualcosa come questo:

parseToInt(toString(System.currentTimeMillis()) + toString(Random.makeInt()))

Farà molta strada. Ovviamente per garantire veramente l'unicità è necessario utilizzare UUID / GUID. Ma possono essere costosi da generare, quanto sopra è probabilmente sufficiente, poiché l'unica possibilità di sovrapposizione è se la generazione casuale avesse un duplicato nello stesso millisecondo.


9
1ms può richiedere molto tempo in alcuni sistemi.
quant_dev

7
Questo in realtà non riduce affatto la possibilità di collisione. La probabilità di una collisione dopo N numeri è esattamente uguale a quella della soluzione originale del PO. Il trucco di utilizzare l'ora corrente come seed viene in genere utilizzato quando si assegnano le chiavi in ​​sequenza.
Cort Ammon,

2
@Fresheyeball Sono fiducioso che non abbia alcun effetto, a meno che Random.makeInt () non generi effettivamente una distribuzione uniforme dal valore minimo dell'intero al valore massimo dell'intero. Per ogni valore passato generato da questa funzione, esiste un valore casuale da makeInt che, per questo preciso passo temporale, genera quel valore, creando una collisione. Poiché tutti i valori di makeInt sono equiprobabili, la probabilità di una collisione è esattamente uguale a quella della probabilità di una collisione senza l'aggiunta di tempo.
Cort Ammon,

2
@CortAmmon questo non usa l'ora corrente come seme , e sicuramente fa la differenza fintanto che quei numeri N non sono stati generati durante lo stesso millisecondo, perché due numeri con parti di data / ora diverse non si scontrano mai . Se immagini l'esempio dell'altra risposta di un pacchetto al secondo con una probabilità di collisione del 50% in meno di un giorno, questo ha una probabilità di collisione dello 0% in un pacchetto al secondo, almeno fino al tempo che lo currentTimeMillisavvolge.
Hobbs,

3
@hobbs Hai dimenticato l'overflow dei numeri interi. Ora, se la chiave utilizzata dall'OP era una struttura contenente 2 numeri interi, uno contenente System.currentTimeMillise uno contenente Random.makeInt(), la probabilità di una collisione diminuisce sostanzialmente. Tuttavia, non è quello che fa il codice in questo esempio. Dato qualsiasi tempo precedente e valore casuale, e qualsiasi tempo corrente, la probabilità di collisione è identica alla probabilità che due numeri casuali si scontrino in primo luogo.
Cort Ammon,

3

Dipende sia dalla probabilità di fallimento che dalle conseguenze del fallimento.

Ricordo un dibattito tra persone del software e dell'hardware in cui le persone dell'hardware consideravano accettabile un algoritmo con una piccola probabilità di risultati errati (qualcosa come 1 errore in 100 anni) e la gente del software pensava che fosse un anatema. Si è scoperto che la gente dell'hardware calcolava abitualmente i tassi di guasto previsti e si era molto abituata all'idea che ogni tanto avrebbe dato risposte errate di tanto in tanto, ad esempio a causa di disturbi causati dai raggi cosmici; hanno trovato strano che i software si aspettassero un'affidabilità del 100%.


1

Certo, hai probabilità piuttosto basse di due numeri interi casuali a 32 bit sequenziali, ma non è del tutto impossibile. La decisione ingegneristica appropriata si basa su quali sarebbero le conseguenze delle collisioni, una stima del volume di numeri che stai generando, la durata in cui è richiesta l'unicità e cosa succede se un utente malintenzionato inizia a tentare di causare collisioni.


0

Può essere accettabile supporre che i numeri casuali saranno univoci ma devi stare attento.

Supponendo che i tuoi numeri casuali siano equamente distribuiti, la probabilità di una collisione è approssimativamente (n 2/2 ) / k dove n è il numero di numeri casuali che generi e k è il numero di possibili valori che un numero "casuale" può assumere.

Non inserisci un numero astronomicamente improbabile, quindi prendiamolo come 1 su 2 30 (all'incirca in un miliardo). Supponiamo inoltre di generare 2 30 pacchetti (se ogni pacchetto rappresenta circa un kilobyte di dati, ciò significa circa un terabyte di dati totali, grandi ma non inimmaginabili). Troviamo che abbiamo bisogno di un numero casuale con almeno 2 89 valori possibili.

Innanzitutto i tuoi numeri casuali devono essere abbastanza grandi. Un numero casuale a 32 bit può avere al massimo 2 32 possibili valori. Per un server impegnato che non è abbastanza vicino.

In secondo luogo, il generatore di numeri casuali deve avere uno stato interno sufficientemente ampio. Se il tuo generatore di numeri casuali ha solo uno stato interno a 32 bit, non importa quanto sia grande il valore che generi da esso otterrai comunque solo 2 32 valori possibili.

In terzo luogo, se hai bisogno che i numeri casuali siano univoci tra le connessioni piuttosto che all'interno di una connessione, il tuo generatore di numeri casuali deve essere ben strutturato. Ciò è particolarmente vero se il programma viene riavviato frequentemente.

In generale, i generatori di numeri casuali "regolari" nei linguaggi di programmazione non sono adatti a tale uso. I generatori di numeri casuali forniti dalle librerie di crittografia sono generalmente.


0

integrato in alcune delle risposte di cui sopra è il presupposto che il generatore di numeri casuali sia effettivamente "piatto" - che la probabilità che uno qualsiasi dei due numeri sia il successivo generato è lo stesso.

Questo probabilmente non è vero per la maggior parte dei generatori di numeri casuali. La maggior parte dei quali utilizza alcuni polinomi di alto ordine applicati ripetutamente a un seme.

Detto questo, ci sono molti sistemi là fuori che dipendono da questo schema, di solito con UUID. Ad esempio, ogni oggetto e risorsa in Second Life ha un UUID a 128 bit, generato casualmente e raramente si scontrano.


0

Molte persone hanno già dato risposte di alta qualità, ma vorrei aggiungere alcuni punti minori: in primo luogo, il punto di @nomadictype sul paradosso del compleanno è eccellente .

Un altro punto: la casualità non è così semplice da generare e definire come la gente potrebbe presumere. (In effetti, sono disponibili test statistici per la casualità ).

Detto questo, è importante essere consapevoli dell'errore del giocatore d'azzardo , che è un errore statistico in cui le persone presumono che gli eventi indipendenti in qualche modo si influenzino a vicenda. Gli eventi casuali sono generalmente statisticamente indipendenti l'uno dall'altro - vale a dire se si genera casualmente un "10" non cambia la probabilità futura di generare più "10" in meno. (Forse qualcuno potrebbe escogitare un'eccezione a quella regola, ma mi aspetto che sarebbe il caso di quasi tutti i generatori di numeri casuali).

Quindi la mia risposta è che se potessi supporre che una sequenza sufficientemente lunga di numeri casuali fosse univoca, non sarebbero davvero numeri casuali perché sarebbe un chiaro schema statistico. Inoltre, ciò implicherebbe che ogni nuovo numero non è un evento indipendente perché se si genera, ad esempio, un 10, ciò significherebbe che la probabilità di generare 10s futuri sarebbe dello 0% (non potrebbe accadere), inoltre ciò significherebbe che aumenteresti le probabilità di ottenere un numero diverso da 10 (ovvero, più numeri genererai, maggiore sarà la probabilità di ciascuno dei numeri rimanenti).

Un'altra cosa da considerare: la possibilità di vincere il Powerball fuori dal gioco in una singola partita è, a mio avviso, circa 1 su 175 milioni. Tuttavia, le probabilità che qualcuno vinca sono considerevolmente più alte. Sei più interessato alle probabilità che qualcuno "vinca" (ovvero essere un duplicato) che alle probabilità di un determinato numero "vincente" / essere un duplicato.


Se uno sta generando identificatori a 4096 bit in modo tale che ogni bit abbia la stessa probabilità di essere 0 o 1 indipendente da qualsiasi altro bit che è stato generato nello stesso o in qualsiasi altro identificatore, la probabilità che due identificatori corrispondano mai essere sparitamente piccolo anche se si generasse in modo casuale un identificatore diverso per ciascuno degli atomi all'incirca 4.0E81 nell'universo osservabile. Il fatto che tali identificatori sarebbero quasi certamente unici non li renderebbe in alcun modo "non casuali"
supercat

@supercat È vero - dato un numero sufficientemente grande è altamente improbabile che ci siano duplicati, ma non è impossibile. Dipende davvero da quanto siano gravi le conseguenze della non unicità se ciò che l'OP sta descrivendo è una buona idea.
EJoshuaS - Ripristina Monica il

Se la probabilità di una collisione casuale casuale è inferiore alla probabilità di un meteorite che annulla i dispositivi che si basano su ID unici, dal punto di vista ingegneristico non è necessario preoccuparsi del primo. Ci sarebbe un grande bisogno di preoccuparsi di tutto ciò che potrebbe far sì che i numeri casuali non siano indipendenti, ma le collisioni casuali non sarebbero un problema.
supercat

@supercat Penso che tu stia leggendo male questo, vedi l'altra risposta sul paradosso del compleanno, penso che una collisione sia molto più probabile di quanto stai calcolando: l'OP sta usando solo un numero a 32 bit, quindi non sono sicuro di dove tu ' ottenendo 4096 da, e come il nomadictype ha mostrato che la probabilità di un'eventuale collisione con un numero di quella lunghezza è in realtà sorprendentemente alta.
EJoshuaS - Ripristina Monica il

Hai ragione sul fatto che un numero a 32 bit è troppo breve anche per le piccole popolazioni se le collisioni sono totalmente inaccettabili. Se si utilizza un numero sufficientemente grande, è possibile ridurre la probabilità di collisioni casuali al punto in cui si può presumere in sicurezza che non accadranno e, in molti casi, usare un numero maggiore può essere meglio che tentare di usare altri mezzi di garantendo unicità, poiché quest'ultimo richiede generalmente l'accesso a transizioni di stato che non possono essere annullate o ripristinate, anche se l'orologio di un sistema viene ripristinato o il sistema viene ricaricato da un backup.
supercat

0

Non importa quanti bit usi - NON PUOI garantire che due numeri "casuali" saranno diversi. Invece, ti suggerisco di usare qualcosa come l'indirizzo IP o altro indirizzo di rete del computer e un numero sequenziale, preferibilmente un numero sequenziale HONKIN BIG - 128 bit (ovviamente senza segno) sembra un buon inizio, ma 256 sarebbe meglio.


-1

No certo che no. A meno che non utilizzi campioni senza sostituzione, c'è una possibilità, per quanto piccola, di duplicazioni.

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.