Darf man sich darauf verlassen, dass zufällige Ints einzigartig sind?


42

Ich habe ein Netzwerkprotokoll implementiert und benötige Pakete mit eindeutigen Bezeichnern. Bisher habe ich nur zufällige 32-Bit-Ganzzahlen generiert und angenommen, dass es astronomisch unwahrscheinlich ist, dass es während der Lebensdauer eines Programms / einer Verbindung zu einer Kollision kommt. Wird dies im Produktionscode allgemein als akzeptable Praxis angesehen, oder sollte man ein komplexeres System entwickeln, um Kollisionen zu verhindern?


47
Warum wird eine sequentielle Ganzzahl nicht abgeschnitten?
Whatsisname

20
Warum verwenden Sie nicht einfach ein inkrementierendes int? GUIDs , die die Einzigartigkeit Eigenschaften , die Sie beschreiben, sind 128 Bit groß, nicht 32 haben ausgelegt sind
Robert Harvey

21
Alternativ können Sie jedem angeschlossenen Computer eine Kanalnummer zuweisen und eine fortlaufende Sequenz-ID verwenden. Die beiden kombinierten Nummern (wobei die Kanalnummer die höherwertigen Bits einnimmt) werden zu Ihrer neuen eindeutigen ID.
Robert Harvey

27
Wenn Ihr "Zufallszahlengenerator" garantiert, dass eine bestimmte Zahl erst dann wiederholt wird, wenn jede andere Zahl generiert wurde, handelt es sich um einen sehr schlechten Zufallszahlengenerator! Mit der gleichen Logik, die einzig mögliche „random“ Folge von Münzwürfen würde HTHTHTHTHT sein ....
alephzero

17
"Ich fordere, dass Pakete eindeutige Kennungen haben." Was ist die Folge davon, dass diese Anforderung verletzt wird? Wenn Sie eindeutige Kennungen benötigen , um das Wort genau zu lesen, müssen Sie über ein zentrales System verfügen, das Kennungen entfernt (z. B. wie MACs einzelnen Netzwerkkartenfirmen zugewiesen werden). Höchstwahrscheinlich haben Sie eine weichere Definition von "erfordern". Wenn Sie diese Weichheitsstufe verstehen, ändern sich die Antworten, die Sie erhalten, dramatisch.
Cort Ammon

Antworten:


142

Vorsicht vor dem Geburtstagsparadoxon .

Angenommen, Sie generieren eine Folge von Zufallswerten (einheitlich, unabhängig) aus einer Menge der Größe N (in Ihrem Fall N = 2 ^ 32).

Die Faustregel für das Geburtstagsparadoxon besagt dann, dass, sobald Sie ungefähr sqrt (N) -Werte generiert haben, eine Kollision mit einer Wahrscheinlichkeit von mindestens 50% aufgetreten ist, d. H., Dass mindestens zwei identische Werte im vorliegen erzeugte Sequenz.

Für N = 2 ^ 32 ist sqrt (N) = 2 ^ 16 = 65536. Nachdem Sie also ungefähr 65.000 Bezeichner generiert haben, ist es wahrscheinlicher, dass zwei von ihnen kollidieren als nicht! Wenn Sie eine Kennung pro Sekunde generieren, dauert dies weniger als einen Tag. Es ist unnötig zu erwähnen, dass viele Netzwerkprotokolle viel schneller arbeiten.


11
+1. In meinem letzten Job hat einer unserer Partner diesen Ansatz tatsächlich verwendet, um zufällige Kennungen zu generieren (nicht für Netzwerkpakete, sondern für ein gemeinsam genutztes Geschäftsobjekt, das letztendlich von Endkunden erstellt wurde). Als ich die Daten mit einem Blick darauf abfragte, stellte ich fest, dass durchschnittlich zwei bis drei Paare von Duplikaten pro Tag vorhanden waren. (Glücklicherweise hat dies nur dann zu
Problemen geführt,

6
(Klicken Sie hier, um Mathe zu rendern.) Die Näherung von $ \ sqrt {N} $ ist für jeden Wert bis zu einem konstanten Faktor genau. für $ N = 2 ^ {32} $ ist der tatsächliche Schwellenwert 77164, da dies der kleinste Wert von $ n $ ist, sodass $ \ prod_ {k = 1} ^ {n-1} (1 - k / N) <1 / 2. $
wchargin

4
@wchargin: Es ist wirklich nichts Magisches an der Wahrscheinlichkeit, 0,5 zu treffen. Bemerkenswert ist, dass die Wahrscheinlichkeit mit zunehmendem N relativ schnell zunimmt. Wenn 32-Bit-Bezeichner eine geringfügige, aber nicht unbedeutende Chance auf eine zufällige Kollision hätten, hätte ein 40-Bit-Bezeichner fast keine.
Supercat

3
@supercat: Das ist alles wahr. Ich habe nur gedacht, dass man, wenn man eine solche Konstante
angibt

2
@wchargin: Ich denke lieber darüber nach, wo man anfangen muss, sich über Duplikate Gedanken zu machen. Wenn man weit unter sqrt (N) sinkt, fallen die Kollisionswahrscheinlichkeiten schnell ab, bis zu dem Punkt, an dem man sicher sagen kann, dass sie nicht eintreten werden, es sei denn, es liegt ein schwerwiegender Defekt im Zufallsgenerator vor.
Supercat

12

Es wird allgemein als akzeptabel angesehen, sich darauf zu verlassen, dass Zufallszahlen eindeutig sind, wenn diese Zahlen genügend Bits haben. Es gibt kryptografische Protokolle, bei denen das Wiederholen einer Zufallszahl die gesamte Sicherheit gefährdet. Und solange der verwendete Zufallszahlengenerator keine ernsthaften Sicherheitslücken aufweist, war dies kein Problem.

Einer der Algorithmen zum Generieren von UUIDs generiert effektiv eine ID, die aus 122 zufälligen Bits besteht, und geht davon aus, dass sie eindeutig ist. Zwei der anderen Algorithmen setzen voraus, dass ein auf 122 Bit gekürzter Hash-Wert eindeutig ist, was ungefähr das gleiche Kollisionsrisiko birgt.

Es gibt also Standards, bei denen 122 Bit ausreichen, um eine zufällige ID eindeutig zu machen, aber 32 Bit sind definitiv nicht genug. Bei 32-Bit-IDs dauert es nur etwa 2¹⁶ IDs, bis das Kollisionsrisiko 50% erreicht, da bei 2¹⁶ IDs nahezu 2³¹ Paare vorhanden sind, von denen jedes eine Kollision sein könnte.

Sogar 122 Bits sind weniger, als ich in jedem neuen Design empfehlen würde. Wenn Ihnen die Einhaltung von Standards wichtig ist, verwenden Sie UUIDs. Verwenden Sie andernfalls etwas, das größer als 122 Bit ist.

Die SHA1-Hash-Funktion mit einer Ausgabe von 160 Bit gilt nicht mehr als sicher, was zum Teil daran liegt, dass 160 Bit nicht ausreichen, um die Eindeutigkeit der Ausgaben zu gewährleisten. Moderne Hash-Funktionen haben Ausgänge von 224 bis 512 Bit. Nach dem Zufallsprinzip generierte IDs sollten auf die gleichen Größen abzielen, um die Eindeutigkeit mit einem guten Sicherheitsspielraum zu gewährleisten.


12
SHA-1 gilt als unsicher, weil es bestimmte Angriffe (dh nicht zufällige) gegen den Algorithmus selbst gibt, die Kollisionen schneller als Brute Force finden können, und nicht, weil die Wahrscheinlichkeit einer zufälligen Kollision hoch ist. Eine grobe Schätzung besagt, dass es bei 122 Bits und einer Generierungsrate von 1 Milliarde (10 ^ 9) IDs pro Sekunde über 73 Jahre dauern würde, bis eine Kollisionswahrscheinlichkeit von 50% erreicht ist.
8bittree

sqrt(2^122)= 2,3
Billiarden Billiarden

2
@ 8bittree Das Bitcoin-Netzwerk berechnet alle 10 Minuten 2⁷⁰ SHA2-Hashes. Wäre das SHA1-Hashes gewesen, würde es nur eine Woche dauern, um eine Kollision zu erzeugen. Wenn UUIDs mit der gleichen Geschwindigkeit erstellt würden, mit der Bitcoin Hashes berechnet, würde die Erstellung einer Kollision weniger als 2 Sekunden dauern.
Kasperd

Bitcoin ist alles über versucht Kollisionen zu finden, und ist sehr beliebt und hat sich für die Suche nach Hashes dedizierter Hardware speziell hatte. Nun, sicher, wenn das OP plant, eine äußerst beliebte Kryptowährung oder ähnliches zu erstellen, dann benötigen sie möglicherweise Hunderte oder Tausende von Bits pro ID. Die sofortige Annahme, dass dies die Anforderungen sind, kann jedoch weitaus mehr Arbeit als nötig ermutigen, wenn eine Standard-UUID-Bibliothek ausreicht.
8bittree

@ 8bittree Wenn die Verwendung von Standardbibliotheken von Vorteil ist, entscheiden Sie sich auf jeden Fall für UUID. Das Herausziehen einiger zufälliger Bytes urandomist jedoch nicht mehr Arbeit als die Verwendung einer UUID-Bibliothek. Ich habe gerade beide zum Vergleich in Python implementiert und jede Methode enthielt genau 25 Zeichen Quellcode.
Kasperd

3

Ich würde diese schlechte Praxis nennen. Zufallszahlen erzeugen einfach keine eindeutigen Zahlen, sie erzeugen einfach Zufallszahlen. Eine zufällige Verteilung enthält wahrscheinlich einige Duplikate. Sie können diesen Umstand annehmbar unwahrscheinlich machen, indem Sie ein Zeitelement hinzufügen. Wenn Sie die aktuelle Zeit in Millisekunden von der Systemuhr erhalten. Etwas wie das:

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

Wird einen langen Weg gehen. Um die Eindeutigkeit zu gewährleisten, müssen Sie UUID / GUID verwenden. Die Generierung kann jedoch teuer sein. Das oben Genannte ist wahrscheinlich ausreichend, da die einzige Möglichkeit einer Überlappung darin besteht, dass die zufällige Generierung ein Duplikat in derselben Millisekunde aufwies.


9
1 ms kann in manchen Systemen eine lange Zeit sein.
quant_dev

7
Dies verringert die Kollisionswahrscheinlichkeit überhaupt nicht. Die Wahrscheinlichkeit einer Kollision nach N Zahlen entspricht genau der ursprünglichen Lösung des OP. Der Trick, die aktuelle Zeit als Startwert zu verwenden, wird normalerweise beim sequentiellen Zuweisen von Schlüsseln verwendet.
Cort Ammon

2
@Fresheyeball Ich bin zuversichtlich, dass es keine Auswirkung hat, es sei denn, Random.makeInt () generiert tatsächlich keine gleichmäßige Verteilung vom minimalen Wert der Ganzzahl zum maximalen Wert der Ganzzahl. Für jeden von dieser Funktion generierten früheren Wert gibt es einen Zufallswert von makeInt, der für diesen genauen Zeitschritt diesen Wert generiert und eine Kollision erzeugt. Da alle Werte von makeInt gleich wahrscheinlich sind, ist die Wahrscheinlichkeit einer Kollision genau gleich der Wahrscheinlichkeit einer Kollision ohne den Zusatz von Zeit.
Cort Ammon

2
@CortAmmon Dies ist die aktuelle Zeit als nicht mit Samen , und es ist auf jeden Fall einen Unterschied machen , solange diese N Zahlen nicht alle im gleichen Millisekunden erzeugt wurden, weil zwei Zahlen mit unterschiedlichen Zeitstempeln Teilen nie kollidieren. Wenn Sie sich das Beispiel der anderen Antwort vorstellen, dass ein Paket pro Sekunde eine Kollisionswahrscheinlichkeit von 50% in weniger als einem Tag aufweist, besteht für dieses Paket eine Kollisionswahrscheinlichkeit von 0% bei einem Paket pro Sekunde, zumindest bis zu dem Zeitpunkt, an dem sich die currentTimeMillisDaten ändern.
Hobbs

3
@hobbs Sie haben den Integer-Überlauf vergessen. Wenn der Schlüssel, den das OP verwendete, eine Struktur war, die zwei Ganzzahlen enthielt, eine, die enthielt, System.currentTimeMillisund eine, die enthielt Random.makeInt(), dann sinkt die Wahrscheinlichkeit einer Kollision erheblich. Dies ist jedoch nicht das, was der Code in diesem Beispiel tut. Gegeben beliebigen vorherigen Zeitpunkt und Zufallswert, und jede ist aktuelle Zeit, die Wahrscheinlichkeit einer Kollision identisch mit der Wahrscheinlichkeit von zwei Zufallszahlen in erster Linie kollidieren.
Cort Ammon

3

Dies hängt sowohl von der Ausfallwahrscheinlichkeit als auch von den Folgen des Ausfalls ab.

Ich erinnere mich an eine Debatte zwischen Software- und Hardwareleuten, bei der die Hardwareleute einen Algorithmus mit einer geringen Wahrscheinlichkeit falscher Ergebnisse (etwa 1 Fehler in 100 Jahren) für akzeptabel hielten und die Softwareleute dies für ein Gräuel hielten. Es stellte sich heraus, dass die Hardware-Leute routinemäßig die erwarteten Ausfallraten berechneten und sehr an die Vorstellung gewöhnt waren, dass alles gelegentlich falsche Antworten geben würde, z. B. aufgrund von Störungen, die durch kosmische Strahlung verursacht wurden. Sie fanden es seltsam, dass Software-Leute eine hundertprozentige Zuverlässigkeit erwarteten.


1

Sicher, Sie haben ziemlich niedrige Wahrscheinlichkeiten dafür, dass zwei zufällige 32-Bit-Ganzzahlen sequenziell sind, aber das ist nicht ganz unmöglich. Die geeignete technische Entscheidung basiert auf den Konsequenzen von Kollisionen, einer Schätzung des von Ihnen generierten Zahlenvolumens, der Lebensdauer, für die Eindeutigkeit erforderlich ist, und der Frage, was passiert, wenn ein böswilliger Benutzer versucht, Kollisionen auszulösen.


0

Es ist akzeptabel anzunehmen, dass Zufallszahlen eindeutig sind, aber Sie müssen vorsichtig sein.

Angenommen, Ihre Zufallszahlen sind gleichmäßig verteilt, ist die Wahrscheinlichkeit einer Kollision ungefähr (n 2/2 ) / k, wobei n die Anzahl der von Ihnen generierten Zufallszahlen und k die Anzahl der möglichen Werte ist, die eine "Zufallszahl" annehmen kann.

Sie geben keine astronomisch unwahrscheinliche Zahl an, nehmen wir also 1 zu 2 30 (ungefähr eine Milliarde). Nehmen wir weiter an, Sie generieren 2 bis 30 Pakete (wenn jedes Paket ungefähr ein Kilobyte an Daten darstellt, bedeutet dies ungefähr ein Terabyte an Gesamtdaten, groß, aber nicht unvorstellbar). Wir brauchen eine Zufallszahl mit mindestens 2 89 möglichen Werten.

Erstens müssen Ihre Zufallszahlen groß genug sein. Eine 32-Bit-Zufallszahl kann maximal 2 32 mögliche Werte haben. Für einen ausgelasteten Server, der bei weitem nicht hoch genug ist.

Zweitens muss Ihr Zufallsgenerator einen ausreichend großen internen Zustand haben. Wenn Ihr Zufallszahlengenerator nur einen internen 32-Bit-Zustand hat, erhalten Sie, egal wie groß der Wert ist, den Sie daraus generieren, immer noch höchstens 2 32 mögliche Werte.

Drittens, wenn Sie möchten, dass die Zufallszahlen nicht nur innerhalb einer Verbindung, sondern verbindungsübergreifend eindeutig sind, muss Ihr Zufallszahlengenerator eine gute Ausgangsbasis haben. Dies gilt insbesondere dann, wenn Ihr Programm häufig neu gestartet wird.

Im Allgemeinen sind die "regulären" Zufallszahlengeneratoren in Programmiersprachen für eine solche Verwendung nicht geeignet. Die Zufallszahlengeneratoren, die von Kryptografiebibliotheken bereitgestellt werden, sind im Allgemeinen.


0

In einige der obigen Antworten ist die Annahme eingebaut, dass der Zufallszahlengenerator tatsächlich "flach" ist - dass die Wahrscheinlichkeit, dass zwei beliebige Zahlen die nächste sind, die gleiche ist.

Das gilt wahrscheinlich nicht für die meisten Zufallsgeneratoren. Die meisten verwenden ein Polynom höherer Ordnung, das wiederholt auf einen Samen angewendet wird.

Das heißt, es gibt viele Systeme, die von diesem Schema abhängen, normalerweise mit UUIDs. Beispielsweise hat jedes Objekt und Asset in Second Life eine zufällig generierte 128-Bit-UUID, und sie kollidieren selten.


0

Viele Leute haben bereits qualitativ hochwertige Antworten gegeben, aber ich möchte ein paar kleinere Punkte hinzufügen: Erstens ist @nomadictypes Punkt über das Geburtstagsparadoxon ausgezeichnet .

Ein weiterer Punkt: Zufälligkeit ist nicht so einfach zu generieren und zu definieren, wie es die Leute normalerweise annehmen. (Tatsächlich gibt es statistische Tests für die Zufälligkeit ).

Mit dieser sagte, ist es wichtig , der sich bewusst sein , Fallacy der Gambler , der ein statistischer Irrtum ist , wo die Menschen davon ausgehen , dass unabhängige Ereignisse irgendwie gegenseitig beeinflussen. Zufällige Ereignisse sind in der Regel statistisch unabhängig voneinander. Wenn Sie also zufällig eine "10" generieren, ändert dies nichts an Ihrer zukünftigen Wahrscheinlichkeit, im geringsten mehr "10" zu generieren. (Vielleicht könnte jemand eine Ausnahme von dieser Regel finden, aber ich würde erwarten, dass dies für so ziemlich alle Zufallszahlengeneratoren der Fall ist.)

Wenn Sie also davon ausgehen könnten , dass eine ausreichend lange Folge von Zufallszahlen eindeutig ist, wären dies keine Zufallszahlen, da dies ein klares statistisches Muster wäre. Außerdem würde dies bedeuten, dass jede neue Zahl kein eigenständiges Ereignis ist. Wenn Sie beispielsweise eine 10 generieren, würde dies bedeuten, dass die Wahrscheinlichkeit, zukünftige 10 zu generieren, 0% beträgt (dies könnte möglicherweise nicht passieren) Das würde bedeuten, dass Sie die Wahrscheinlichkeit erhöhen würden, eine andere Zahl als 10 zu erhalten (dh je mehr Zahlen Sie generieren, desto höher ist die Wahrscheinlichkeit für jede der verbleibenden Zahlen).

Noch etwas zu beachten: Die Chance, den Powerball für ein einziges Spiel zu gewinnen, liegt meines Wissens bei etwa 1 zu 175 Millionen. Allerdings sind die Chancen von jemandem gewinnen deutlich höher als das. Sie interessieren sich mehr für die Wahrscheinlichkeit, dass jemand "gewinnt" (dh ein Duplikat ist) als für die Wahrscheinlichkeit, dass eine bestimmte Zahl "gewinnt" / ein Duplikat ist.


Wenn man 4096-Bit-Identifikatoren so generiert, dass jedes Bit gleich wahrscheinlich 0 oder 1 ist, unabhängig von einem anderen Bit, das in demselben oder einem anderen Identifikator generiert wurde, würde die Wahrscheinlichkeit, dass zwei Identifikatoren jemals übereinstimmen, steigen verschwindend klein sein, selbst wenn man zufällig einen anderen Bezeichner für jedes der ungefähr 4.0E81-Atome im beobachtbaren Universum erzeugen würde. Die Tatsache, dass solche Bezeichner mit ziemlicher Sicherheit eindeutig wären, würde sie in keiner Weise "nicht zufällig" machen
supercat

@supercat Das stimmt - bei einer ausreichend großen Zahl ist es sehr unwahrscheinlich, dass es Duplikate gibt, aber es ist nicht unmöglich. Es kommt wirklich darauf an, wie schlimm die Konsequenzen der Nicht-Eindeutigkeit sind, ob das, was das OP beschreibt, eine gute Idee ist.
EJoshuaS - Wiedereinsetzung von Monica

Wenn die Wahrscheinlichkeit einer zufälligen Kollision geringer ist als die Wahrscheinlichkeit eines Meteoritenschlags, der die Geräte auslöscht, die auf den eindeutigen IDs beruhen, besteht aus technischer Sicht kein Grund zur Sorge. Es wäre sehr wichtig, sich um alles zu kümmern, was dazu führen könnte, dass die Zufallszahlen nicht unabhängig sind, aber zufällige Kollisionen wären kein Problem.
Supercat

@supercat Ich denke, Sie verstehen das falsch, siehe die andere Antwort auf das Geburtstagsparadoxon. Ich denke, eine Kollision ist weitaus wahrscheinlicher als Sie es kalkulieren. Das OP verwendet nur eine 32-Bit-Zahl. re bekommen 4096 aus, und wie nomadictype zeigte, ist die Wahrscheinlichkeit einer eventuellen Kollision mit einer Nummer dieser Länge tatsächlich überraschend hoch.
EJoshuaS - Wiedereinsetzung von Monica

Sie haben Recht, dass eine 32-Bit-Zahl selbst für kleine Populationen zu kurz ist, wenn Kollisionen völlig inakzeptabel sind. Wenn man eine ausreichend große Zahl verwendet, kann man die Wahrscheinlichkeit von zufälligen Kollisionen so weit reduzieren, dass man davon ausgehen kann, dass sie einfach nicht mehr passieren. In vielen Fällen ist die Verwendung einer größeren Zahl besser als die Verwendung anderer Mittel Gewährleistung der Eindeutigkeit, da letztere im Allgemeinen den Zugriff auf Statusübergänge erfordert, die nicht rückgängig gemacht oder zurückgesetzt werden können, selbst wenn die Systemuhr zurückgesetzt oder das System von einer Sicherung neu geladen wird.
Supercat

0

Es spielt keine Rolle, wie viele Bits Sie verwenden - Sie können NICHT garantieren, dass zwei "Zufallszahlen" unterschiedlich sind. Stattdessen schlage ich vor, dass Sie so etwas wie die IP-Adresse oder eine andere Netzwerkadresse des Computers und eine fortlaufende Nummer verwenden, vorzugsweise eine HONKIN 'BIG-fortlaufende Nummer - 128 Bit (offensichtlich ohne Vorzeichen) klingen nach einem guten Start, aber 256 wären besser.


-1

Nein natürlich nicht. Sofern Sie keine ersatzlosen Samples verwenden, besteht die Möglichkeit einer - wenn auch geringen - Duplizierung.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.