¿Aceptable confiar en que las entradas aleatorias sean únicas?


42

He estado implementando un protocolo de red y requiero que los paquetes tengan identificadores únicos. Hasta ahora, he estado generando enteros aleatorios de 32 bits, y suponiendo que es astronómicamente improbable que haya una colisión durante la vida útil de un programa / conexión. ¿Se considera esto generalmente una práctica aceptable en el código de producción, o se debería idear un sistema más complejo para evitar colisiones?


47
¿Por qué usar un número entero secuencial no va a cortarlo?
whatsisname

20
¿Por qué no solo usas un int incremental? Los GUID , que están diseñados para tener las propiedades de unicidad que usted describe, tienen un tamaño de 128 bits, no 32.
Robert Harvey

21
Alternativamente, asigne un número de canal a cada computadora conectada y use una identificación de secuencia incremental. Los dos números combinados (con el número de canal ocupando los bits de orden superior) se convierten en su nueva identificación única.
Robert Harvey

27
Si su "generador de números aleatorios" garantiza que un número particular no se repetirá hasta que se haya generado cualquier otro número, ¡es un generador de números aleatorios muy pobre! Por la misma lógica, la única secuencia "aleatoria" posible de lanzamiento de monedas sería HTHTHTHTHT ....
alephzero

17
"Exijo que los paquetes tengan identificadores únicos" ¿Cuál es la consecuencia de la violación de este requisito? Si necesita identificadores únicos, en la lectura más estricta de la palabra, debe tener un sistema centralizado que proporcione identificadores (como cómo se asignan los MAC a las compañías de tarjetas de red individuales). Lo más probable es que tenga una definición más suave de "requerir". Comprender ese nivel de suavidad cambiará drásticamente las respuestas que reciba.
Cort Ammon

Respuestas:


142

Cuidado con la paradoja del cumpleaños .

Suponga que está generando una secuencia de valores aleatorios (uniformemente, independientemente) a partir de un conjunto de tamaño N (N = 2 ^ 32 en su caso).

Luego, la regla general para la paradoja del cumpleaños establece que una vez que haya generado aproximadamente valores de sqrt (N), hay al menos un 50% de posibilidades de que se haya producido una colisión, es decir, que haya al menos dos valores idénticos en el secuencia generada

Para N = 2 ^ 32, sqrt (N) = 2 ^ 16 = 65536. Entonces, después de haber generado unos 65k identificadores, ¡es más probable que dos de ellos choquen! Si genera un identificador por segundo, esto sucedería en menos de un día; No hace falta decir que muchos protocolos de red funcionan mucho más rápido que eso.


11
+1. En mi último trabajo, uno de nuestros socios utilizó este enfoque para generar identificadores aleatorios (no para paquetes de red, sino para un objeto comercial compartido creado en última instancia por los clientes finales). Cuando pregunté los datos con miras a esto, descubrí que, en promedio, había dos o tres pares de duplicados cada día. (Afortunadamente, esto solo daña las cosas si los duplicados se crearon con cuatro horas de diferencia, lo que sucedió con un poco menos de frecuencia. Pero aún así.)
ruakh

66
(haga clic aquí para representar las matemáticas) Para lo que vale, la aproximación $ \ sqrt {N} $ es precisa hasta un factor constante; para $ N = 2 ^ {32} $, el umbral real es 77164, ya que este es el valor más pequeño de $ n $ tal que $ \ prod_ {k = 1} ^ {n-1} (1 - k / N) <1 / 2. $
wchargin

44
@wchargin: Realmente no hay nada mágico sobre la probabilidad de llegar a 0.5; lo que es notable es que la probabilidad aumenta relativamente rápido con el aumento de N. Si los identificadores de 32 bits tendrían una posibilidad leve pero no trivial de una colisión aleatoria, un identificador de 40 bits casi no tendría ninguno.
supercat

3
@supercat: Eso es todo cierto. Me acabo de dar cuenta de que si uno proporciona una constante de este tipo, también podría dar un valor exacto :-)
wchargin

2
@wchargin: prefiero pensar en términos de dónde uno debe comenzar a preocuparse por los duplicados. Si uno va muy por debajo de sqrt (N), las probabilidades de colisiones disminuyen rápidamente, hasta el punto de que se puede decir con seguridad que no sucederán a menos que haya un defecto grave en el generador aleatorio.
supercat

12

En general, se considera aceptable confiar en que los números aleatorios sean únicos si esos números tienen suficientes bits. Hay protocolos criptográficos donde la repetición de un número aleatorio romperá toda la seguridad. Y siempre que no haya vulnerabilidades graves en el generador de números aleatorios que se está utilizando, eso no ha sido un problema.

Uno de los algoritmos para generar UUID generará efectivamente una ID que consta de 122 bits aleatorios y supondrá que será único. Y dos de los otros algoritmos se basan en un valor hash truncado a 122 bits que es único, lo que tiene aproximadamente el mismo riesgo de colisiones.

Por lo tanto, hay estándares que dependen de que 122 bits sean suficientes para hacer que una identificación aleatoria sea única, pero 32 bits definitivamente no es suficiente. Con ID de 32 bits, solo se necesitan aproximadamente 2¹⁶ ID antes de que el riesgo de colisión alcance el 50% porque con 2¹⁶ ID habrá cerca de 2³¹ pares, cada uno de los cuales podría ser una colisión.

Incluso 122 bits es menos de lo que recomendaría en cualquier diseño nuevo. Si seguir alguna estandarización es importante para usted, use UUID. De lo contrario, use algo más grande que 122 bits.

La función hash SHA1 con una salida de 160 bits ya no se considera segura, lo que se debe en parte a que 160 bits no es suficiente para garantizar la unicidad de las salidas. Las funciones hash modernas tienen salidas de 224 a 512 bits. Las ID generadas aleatoriamente deben apuntar a los mismos tamaños para garantizar la unicidad con un buen margen de seguridad.


12
SHA-1 se considera inseguro porque hay ataques específicos (es decir, no aleatorios) contra el algoritmo en sí mismo que pueden encontrar colisiones más rápido que la fuerza bruta, no porque haya una alta probabilidad de una colisión aleatoria. Una estimación aproximada dice que con 122 bits y una tasa de generación de mil millones (10 ^ 9) ID por segundo, tomaría más de 73 años antes de alcanzar un 50% de posibilidades de colisión.
8bittree

sqrt(2^122)= 2.3
billones de billones de

2
@ 8bittree La red bitcoin calcula 2⁷⁰ hash SHA2 cada 10 minutos. De haber sido hash SHA1, solo tomaría una semana producir una colisión. Si los UUID se produjeran a la misma velocidad con la que Bitcoin calcula los hash, tomaría menos de 2 segundos producir una colisión.
Kasperd

Bitcoin se trata de tratar de encontrar colisiones, y es inmensamente popular y ha tenido un hardware dedicado diseñado específicamente para encontrar hashes. Ahora, claro, si el OP está planeando crear una criptomoneda muy popular, o algo similar, entonces podrían necesitar cientos o miles de bits por ID. Pero asumir de inmediato que esos son los requisitos podría alentar mucho más trabajo del necesario si una biblioteca UUID estándar es suficiente.
8bittree

@ 8bittree Si el uso de bibliotecas estándar es una ventaja, entonces vaya por UUID. Pero extraer algunos bytes aleatorios urandomno es más trabajo que usar una biblioteca UUID. Acabo de implementar ambos en Python para comparar, y cada método tenía exactamente 25 caracteres de código fuente.
kasperd

3

Yo llamaría a esto una mala práctica. El número aleatorio genera simplemente no crea números únicos, solo crea números aleatorios. Es probable que una distribución aleatoria incluya algunos duplicados. Puede hacer que esta circunstancia sea aceptablemente improbable agregando un elemento de tiempo. Si obtiene la hora actual del reloj del sistema en milisegundos. Algo como esto:

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

Recorreremos un largo camino. Obviamente, para garantizar realmente la unicidad, necesita usar UUID / GUID. Pero pueden ser costosos de generar, lo anterior es probablemente suficiente, ya que la única posibilidad de superposición es si la generación aleatoria tuvo un duplicado en el mismo milisegundo.


99
1 ms puede ser mucho tiempo en algunos sistemas.
quant_dev

77
Esto en realidad no disminuye la posibilidad de colisión en absoluto. La probabilidad de una colisión después de N números es exactamente igual a la de la solución original del OP. El truco de usar el tiempo actual como semilla se usa típicamente cuando se asignan claves secuencialmente.
Cort Ammon

2
@Fresheyeball Estoy seguro de que no tiene ningún efecto, a menos que Random.makeInt () en realidad no genere una distribución uniforme del valor mínimo del entero al valor máximo del entero. Para cada valor pasado generado por esta función, hay un valor aleatorio de makeInt que, para este paso de tiempo exacto, genera ese valor, creando una colisión. Como todos los valores de makeInt son equiprobables, la probabilidad de una colisión es exactamente igual a la de la probabilidad de una colisión sin la adición de tiempo.
Cort Ammon

2
@CortAmmon, esto no está usando el tiempo actual como semilla , y definitivamente hace una diferencia siempre que esos números N no se generen durante el mismo milisegundo, porque dos números con partes de marca de tiempo diferentes nunca chocan. Si imagina el ejemplo de la otra respuesta de un paquete por segundo que tiene un 50% de posibilidades de colisión en menos de un día, este tiene un 0% de posibilidades de colisión en un paquete por segundo, al menos hasta el momento en que se currentTimeMillisenvuelve.
hobbs

3
@hobbs Te olvidas del desbordamiento de enteros. Ahora, si la clave que utilizó el OP fue una estructura que contiene 2 enteros, uno que contiene System.currentTimeMillisy otro que contiene Random.makeInt(), entonces la probabilidad de una colisión disminuye sustancialmente. Sin embargo, eso no es lo que hace el código en este ejemplo. Dado cualquier tiempo anterior y valor aleatorio, y cualquier tiempo actual, la probabilidad de colisión es idéntica a la probabilidad de que dos números aleatorios colisionen en primer lugar.
Cort Ammon

3

Depende tanto de la probabilidad de falla como de las consecuencias de la falla.

Recuerdo un debate entre personas de software y hardware donde las personas de hardware consideraron que un algoritmo con una pequeña probabilidad de resultados incorrectos (algo así como 1 falla en 100 años) era aceptable, y las personas de software pensaron que esto era un anatema. Resultó que la gente del hardware calculaba rutinariamente las tasas de falla esperadas, y estaban muy acostumbrados a la idea de que todo daría respuestas incorrectas ocasionalmente, por ejemplo, debido a las perturbaciones causadas por los rayos cósmicos; les pareció extraño que la gente de software esperara una fiabilidad del 100%.


1

Claro, tienes probabilidades bastante bajas de que dos enteros aleatorios de 32 bits sean secuenciales, pero no es completamente imposible. La decisión de ingeniería apropiada se basa en cuáles serían las consecuencias de las colisiones, una estimación del volumen de números que está generando, la vida útil durante la cual se requiere unicidad y qué sucede si un usuario malintencionado comienza a intentar causar colisiones.


0

Puede ser aceptable suponer que los números aleatorios serán únicos, pero hay que tener cuidado.

Suponiendo que sus números aleatorios están distribuidos equitativamente, la probabilidad de una colisión es aproximadamente (n 2/2 ) / k donde n es el número de números aleatorios que genera yk es el número de valores posibles que puede tomar un número "aleatorio".

No pone un número en astronómicamente improbable, así que tomemos como 1 en 2 30 (aproximadamente en mil millones). Digamos además que genera 2 30 paquetes (si cada paquete representa aproximadamente un kilobyte de datos, esto significa aproximadamente un terabyte de datos totales, grandes pero no inimaginablemente). Encontramos que necesitamos un número aleatorio con al menos 2 89 valores posibles.

En primer lugar, tus números aleatorios deben ser lo suficientemente grandes. Un número aleatorio de 32 bits puede tener como máximo 2 32 valores posibles. Para un servidor ocupado que no está lo suficientemente cerca.

En segundo lugar, su generador de números aleatorios debe tener un estado interno suficientemente grande. Si su generador de números aleatorios solo tiene un estado interno de 32 bits, no importa cuán grande sea el valor que genere, solo obtendrá como máximo 2 32 valores posibles.

En tercer lugar, si necesita que los números aleatorios sean únicos en todas las conexiones en lugar de solo dentro de una conexión, su generador de números aleatorios debe estar bien sembrado. Esto es especialmente cierto si su programa se reinicia con frecuencia.

En general, los generadores de números aleatorios "regulares" en lenguajes de programación no son adecuados para tal uso. Los generadores de números aleatorios proporcionados por las bibliotecas de criptografía generalmente son.


0

En algunas de las respuestas anteriores se asume que el generador de números aleatorios es realmente 'plano', que la probabilidad de que dos números sean el próximo generado es la misma.

Eso probablemente no sea cierto para la mayoría de los generadores de números aleatorios. La mayoría de los cuales utilizan algún polinomio de alto orden aplicado repetidamente a una semilla.

Dicho esto, hay muchos sistemas que dependen de este esquema, generalmente con UUID. Por ejemplo, cada objeto y activo en Second Life tiene un UUID de 128 bits, generado aleatoriamente, y rara vez chocan.


0

Mucha gente ya ha dado respuestas de alta calidad, pero me gustaría agregar algunos puntos menores: primero, el punto de @nomadictype sobre la paradoja del cumpleaños es excelente .

Otro punto: la aleatoriedad no es tan sencilla de generar y definir como la gente podría suponer. (De hecho, en realidad hay pruebas estadísticas de aleatoriedad disponibles).

Dicho esto, es importante tener en cuenta la Falacia del jugador , que es una falacia estadística en la que las personas suponen que los eventos independientes de alguna manera se influyen entre sí. Los eventos aleatorios generalmente son estadísticamente independientes entre sí, es decir, si genera aleatoriamente un "10", no cambia su probabilidad futura de generar más "10" en lo más mínimo. (Tal vez alguien podría presentar una excepción a esa regla, pero esperaría que ese fuera el caso para casi todos los generadores de números aleatorios).

Entonces, mi respuesta es que si pudieras asumir que una secuencia suficientemente larga de números aleatorios es única, en realidad no serían números aleatorios porque sería un patrón estadístico claro. Además, implicaría que cada nuevo número no es un evento independiente porque si genera, por ejemplo, un 10, eso significaría que la probabilidad de generar futuros 10 sería del 0% (posiblemente no podría suceder), más eso significaría que aumentaría las probabilidades de obtener un número distinto de 10 (es decir, cuantos más números genere, mayor será la probabilidad de que cada uno de los números restantes se vuelva).

Una cosa más a considerar: la posibilidad de ganar el Powerball de jugar un solo juego es, según tengo entendido, aproximadamente 1 en 175 millones. Sin embargo, las probabilidades de que alguien gane son considerablemente más altas que eso. Está más interesado en las probabilidades de que alguien "gane" (es decir, que sea un duplicado) que en las probabilidades de que un número particular "gane" / sea un duplicado.


Si uno está generando identificadores de 4096 bits de tal manera que cada bit es igualmente probable que sea 0 o 1 independiente de cualquier otro bit que se haya generado en el mismo identificador o en cualquier otro, la probabilidad de que coincidan dos identificadores ser extremadamente pequeño, incluso si uno generara aleatoriamente un identificador diferente para cada uno de los átomos de aproximadamente 4.0E81 en el universo observable. El hecho de que tales identificadores sean ciertamente únicos no los haría "no aleatorios" de ninguna manera
supercat

@supercat Eso es cierto: dado un número suficientemente grande, es muy poco probable que haya duplicados, pero no es imposible. Realmente depende de cuán malas sean las consecuencias de la no unicidad si lo que describe el OP es una buena idea.
EJoshuaS - Restablece a Mónica el

Si la probabilidad de una colisión aleatoria es menor que la probabilidad de que un meteorito destruya los dispositivos que dependen de los identificadores únicos, desde una perspectiva de ingeniería no hay necesidad de preocuparse por el primero. Habrá una gran necesidad de preocuparse por cualquier cosa que pueda hacer que los números aleatorios no sean independientes, pero las colisiones aleatorias no serán un problema.
supercat

@supercat Creo que estás leyendo mal esto, mira la otra respuesta en la paradoja del cumpleaños, creo que una colisión es mucho más probable de lo que estás calculando: el OP solo está usando un número de 32 bits, así que no estoy seguro de dónde estás ' estamos obteniendo 4096 y, como el tipo nómada mostró, la probabilidad de una eventual colisión con un número de esa longitud es en realidad sorprendentemente alta.
EJoshuaS - Restablece a Mónica el

Tiene razón en que un número de 32 bits es demasiado corto incluso para poblaciones pequeñas si las colisiones son totalmente inaceptables. Si uno usa un número que es lo suficientemente grande, puede reducir la probabilidad de colisiones aleatorias hasta el punto en que uno puede asumir con seguridad que simplemente no sucederán, y en muchos casos, usar un número mayor puede ser mejor que tratar de usar otros medios de asegurando la unicidad, ya que este último generalmente requiere tener acceso a las transiciones de estado que no se pueden deshacer o revertir, incluso si el reloj del sistema se reinicia o el sistema se vuelve a cargar desde una copia de seguridad.
supercat

0

No importa cuántos bits use; NO PUEDE garantizar que dos números "aleatorios" sean diferentes. En cambio, sugiero que use algo como la dirección IP u otra dirección de red de la computadora y un número secuencial, preferiblemente un número secuencial HONKIN 'BIG: 128 bits (obviamente sin signo) suena como un buen comienzo, pero 256 sería mejor.


-1

No claro que no. A menos que el rng esté usando muestras sin reemplazo, existe la posibilidad, por pequeña que sea, de duplicación.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.