ランダムな整数が一意であることに依存しても問題ありませんか?


42

ネットワークプロトコルを実装しており、パケットに一意の識別子が必要です。これまでのところ、ランダムな32ビット整数を生成しており、プログラム/接続の存続期間中に衝突が発生する可能性は天文学的に低いと想定しています。これは一般に、本番コードで受け入れられるプラクティスと考えられていますか、それとも衝突を防ぐためにより複雑なシステムを考案する必要がありますか?


47
なぜ連続整数を使用しないのですか?
whatsisname 16

20
インクリメントの整数を使用しないのはなぜですか? 説明する一意性プロパティを持つように設計されたGUIDは、32ビットではなく128ビットのサイズです。
Robert Harvey

21
または、接続されている各コンピューターにチャネル番号を割り当て、増分シーケンスIDを使用します。結合された2つの数値(チャネル番号が上位ビットを占める)が新しい一意のIDになります。
ロバートハーベイ

27
「乱数ジェネレーター」が、特定の数値が1つおきに生成されるまで繰り返されないことを保証している場合、それは非常に貧弱な乱数ジェネレーターです。同じ論理によって、コイン投げの唯一の可能な「ランダム」配列はHTHTHTHTHTあろう....
alephzero

17
「パケットに一意の識別子が必要です」この要件に違反した結果はどうなりますか?一意の識別子が必要な場合は、単語を最も厳密に読むには、識別子を抽出する集中システムが必要です(個々のネットワークカード会社へのMACの割り当て方法など)。ほとんどの場合、「必須」の定義がよりソフトになっています。その柔らかさのレベルを理解すると、受け取る答えが劇的に変わります。
コートアンモン

回答:


142

誕生日のパラドックスに注意してください。

サイズNのセット(ランダムな場合はN = 2 ^ 32)から一連のランダムな値を(一様に、独立して)生成するとします。

次に、誕生日のパラドックスの経験則では、sqrt(N)値について生成すると、少なくとも50%の確率で衝突が発生する、つまり、少なくとも2つの同一の値が生成されたシーケンス。

N = 2 ^ 32、sqrt(N)= 2 ^ 16 = 65536の場合、約65kの識別子を生成した後、2つの識別子が衝突する可能性が高くなります。1秒あたりの識別子を生成する場合、これは1日以内に発生します。言うまでもなく、多くのネットワークプロトコルはそれよりずっと高速に動作します。


11
+1。私の最後の仕事で、パートナーの1人が実際にこのアプローチを使用してランダム識別子を生成しました(ネットワークパケットではなく、最終的にエンドカスタマーによって作成された共有ビジネスオブジェクト)。これに目を向けてデータを照会すると、平均して、毎日2〜3組の複製が存在することがわかりました。(幸いなことに、これは複製が互いに4時間以内に作成された場合にのみ
問題を引き起こしまし

6
(ここをクリックして数学をレンダリングします)価値があるものについては、$ \ sqrt {N} $近似は定数因子まで正確です。$ N = 2 ^ {32} $の場合、実際のしきい値は77164です。これは、$ \ prod_ {k = 1} ^ {n-1}(1-k / N)となる$ n $の最小値であるためです。 <1/2 $
wchargin

4
@wchargin:確率が0.5に達することに関して魔法のようなことは何もありません。注目すべきは、Nの増加に伴って確率が比較的速く増加していることです。32ビットの識別子がランダムな衝突のわずかではあるが重要な機会を持っている場合、40ビットの識別子はほとんどありません。
supercat

3
@supercat:それはすべて真実です。そのような定数を提供すれば、正確な値を提供できるかもしれないと考えただけです:
wchargin

2
@wchargin:重複の心配をどこから始める必要があるのか​​を考えるのが好きです。sqrt(N)を大幅に下回ると、衝突の確率は急速に低下し、ランダムジェネレーターに重大な欠陥がなければ発生しないと安全に言うことができます。
supercat

12

乱数に十分なビットがある場合、一意である乱数に依存することは広く受け入れられると考えられています。乱数を繰り返すとセキュリティ全体が破壊される暗号化プロトコルがあります。使用されている乱数ジェネレーターに深刻な脆弱性がない限り、それは問題ではありません。

UUIDを生成するアルゴリズムの1つは、122のランダムビットで構成されるIDを効果的に生成し、一意であると想定します。また、他の2つのアルゴリズムは、122ビットに切り捨てられたハッシュ値が一意であることに依存しています。これは、衝突のリスクとほぼ同じです。

したがって、ランダムIDを一意にするのに十分な122ビットに依存する規格がありますが、32ビットでは間違いなく十分ではありません。32ビットIDの場合、衝突のリスクが50%に達するまでに約2¹takes IDしかかかりません。これは、2¹with IDの場合、それぞれ衝突の可能性がある2³¹のペアに近いためです。

122ビットでさえ、新しいデザインで推奨するよりも少ないです。何らかの標準化に従うことが重要な場合は、UUIDを使用してください。それ以外の場合は、122ビットより大きいものを使用します。

160ビットの出力を持つSHA1ハッシュ関数は、160ビットでは出力の一意性を保証するには不十分であるため、安全であるとは見なされなくなりました。最新のハッシュ関数の出力は224〜512ビットです。ランダムに生成されたIDは、同じサイズを目指して、十分な安全マージンで一意性を確保する必要があります。


12
SHA-1は、ランダムな衝突の可能性が高いためではなく、ブルートフォースよりも速く衝突を見つけることができるアルゴリズム自体に対する特定の攻撃(ランダムでない)があるため、安全ではないと見なされます。大まかな見積もりでは、122ビットと1秒あたり10億(10 ^ 9)IDの生成レートでは、衝突の50%の可能性に達するまでに73年以上かかります。
8ビットツリー16

sqrt(2^122)= 2.3兆の兆のUUIDの
noɥʇʎԀʎzɐɹƆ

2
@ 8bittreeビットコインネットワークは、10分ごとに2⁷⁰SHA2ハッシュを計算します。それがSHA1ハッシュだったら、衝突を起こすのに1週間しかかかりません。UUIDがビットコインがハッシュを計算するのと同じ速度で生成された場合、衝突を生成するのに2秒もかかりません。
カスペルド16

ビットコインは衝突を発見しようとするものであり、非常に人気があり、ハッシュを見つけるために特別に設計された専用ハードウェアがあります。確かに、OPが非常に人気のある暗号通貨などを作成することを計画している場合、IDごとに数百または数千ビットが必要になる可能性があります。ただし、標準のUUIDライブラリで十分な場合は、これらが要件であるとすぐに想定すると、必要以上に多くの作業が必要になる可能性があります。
8ビットツリー16

@ 8bittree標準ライブラリを使用することが有利な場合は、必ずUUIDを使用してください。しかし、いくつかのランダムなバイトを取り出すことはurandom、UUIDライブラリを使用するよりも多くの作業ではありません。比較のために両方をPythonで実装したところ、各メソッドは正確に25文字のソースコードでした。
カスペルド16

3

私はこれを悪い習慣と呼びます。乱数生成では、単に一意の番号を作成するのではなく、単に乱数を作成します。ランダムな分布には、いくつかの重複が含まれる可能性があります。時間の要素を追加することで、この状況を受け入れ難くすることができます。システムクロックからミリ秒単位で現在の時刻を取得する場合。このようなもの:

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

長い道のりを歩みます。一意性を本当に保証するには、明らかにUUID / GUIDを使用する必要があります。しかし、それらは生成するのに費用がかかる可能性があります。重複の唯一の可能性は、ランダム生成が同じミリ秒で重複していた場合であるため、上記はおそらく十分です。


9
一部のシステムでは、1msの時間がかかる場合があります。
quant_dev 16

7
これは実際には衝突の可能性をまったく減少させません。N個の数字の後の衝突の確率は、OPの元の解の確率とまったく同じです。現在の時刻をシードとして使用するトリックは、通常、キーを順番に割り当てるときに使用されます。
コートアンモン

2
@Fresheyeball Random.makeInt()が整数の最小値から整数の最大値まで均一な分布を実際に生成しない限り、効果がないと確信しています。この関数によって生成された過去の値ごとに、makeIntからのランダムな値があり、この正確なタイムステップでその値が生成され、衝突が発生します。makeIntのすべての値は同等であるため、衝突の確率は、時間を追加しない場合の衝突の確率とまったく同じです。
コートアンモン

2
@CortAmmonはこれをシードとして現在の時間を使用しておらず、タイムスタンプ部分の異なる2つの数値が衝突しないため、同じミリ秒の間にそれらのN個の数値がすべて生成されない限り、間違いなく違いを生じます。他の回答の例では、1秒あたり1パケットが1日以内に50%の衝突の可能性があるという例を想像すると、少なくとも1秒あたり1パケットで、少なくともcurrentTimeMillisラップアラウンドするまでは0%の衝突の可能性があります。
ホッブズ

3
@hobbs整数オーバーフローを忘れます。今キーを使用OPは2つの整数、一方を含有含む構造体だった場合System.currentTimeMillisと含有するものをRandom.makeInt()、そして衝突の確率は、実質的にダウンしました。ただし、この例のコードはそれを行いません。与えられた任意の前回とランダム値、および任意の現在の時刻、衝突の確率は、最初の場所で衝突2個の乱数の確率と同じです。
コートアンモン

3

失敗の確率と失敗の結果の両方に依存します。

ソフトウェアとハ​​ードウェアの人々の間で、間違った結果(100年で1回の失敗など)の可能性が低いアルゴリズムが許容可能であるとハードウェアの人々が考えた議論を覚えています。ハードウェア関係者は予想される故障率を定期的に計算し、たとえば宇宙線によって引き起こされた妨害のために、すべてが時々間違った答えを出すという考えに非常に慣れていることが判明しました。ソフトウェアの人々が100%の信頼性を期待しているのは奇妙だとわかりました。


1

確かに、2つのランダムな32ビット整数が連続している可能性はかなり低いですが、完全に不可能ではありません。適切なエンジニアリングの決定は、衝突の結果、発生する数値の推定、一意性が必要とされる寿命、および悪意のあるユーザーが衝突を試み始めた場合に何が起こるかに基づいています。


0

乱数は一意であると仮定することは許容できますが、注意する必要があります。

あなたの乱数が均等に分散されていると仮定すると、衝突の確率はおおよそ(nは2 /2)/ Kここで、nあなたが生成する乱数の数であり、kは、「ランダム」数が取ることができる可能な値の数です。

あなたは天文学的に考えられないほど数字をつけないので、それを2 30分の 1 (およそ10億で)と考えてみましょう。さらに、2 30個のパケットを生成するとします(各パケットが約1キロバイトのデータを表す場合、これは約1テラバイトの合計データを意味しますが、想像できないほど大きくはありません)。少なくとも2 89の可能な値を持つ乱数が必要であることがわかりました。

まず、乱数は十分に大きくする必要があります。32ビットの乱数は、最大2 32の可能な値を持つことができます。十分な高さのないビジーなサーバーの場合。

次に、乱数ジェネレーターの内部状態を十分に大きくする必要があります。乱数ジェネレーターの内部状態が32ビットのみの場合、そこから生成する値の大きさに関係なく、可能な最大値は2 32個になります。

第三に、接続内だけでなく接続全体で乱数を一意にする必要がある場合は、乱数ジェネレーターを適切にシードする必要があります。これは、プログラムが頻繁に再起動される場合に特に当てはまります。

一般に、プログラミング言語の「通常の」乱数ジェネレーターは、このような使用には適していません。暗号化ライブラリによって提供される乱数ジェネレーターは、一般的にあります。


0

上記の回答のいくつかに組み込まれているのは、乱数ジェネレーターが実際に「フラット」であるという仮定です。2つの数値が次に生成される確率は同じであるというものです。

おそらくほとんどの乱数ジェネレーターには当てはまりません。そのほとんどは、シードに繰り返し適用される高次多項式を使用します。

とは言っても、通常はUUIDを使用するこのスキームに依存するシステムは数多くあります。たとえば、Second Lifeのすべてのオブジェクトとアセットには、ランダムに生成された128ビットのUUIDがあり、めったに衝突しません。


0

多くの人がすでに高品質の答えを出していますが、いくつかの小さな点を付け加えたいと思います。まず、誕生日のパラドックスに関する@nomadictypeの点は素晴らしいです。

もう1つのポイント:ランダム性は、人々が通常想定するほど生成や定義が簡単ではありません。(実際、利用可能なランダム性の統計的検定が実際にあります)。

そうは言っても、ギャンブラーの誤、に注意することは重要です。ギャンブラーの誤acyは、独立したイベントが何らかの形で互いに影響し合うと人々が推測する統計上の誤acyです。ランダムイベントは通常、統計的に互いに独立しています。つまり、「10」をランダムに生成した場合、少なくとも「10」を生成する将来の確率は変わりません。(たぶん誰かがその規則の例外を思いつくかもしれませんが、私はそれがほとんどすべての乱数ジェネレーターの場合に当てはまると期待しています)。

したがって、私の答えは、十分に長い乱数のシーケンスが一意であると仮定できる場合、それは明確な統計パターンになるため、実際には乱数ではないということです。また、たとえば、10を生成すると、将来の10を生成する確率が0%(発生する可能性は低い)になるため、新しい番号はそれぞれ独立したイベントではないことを意味します。つまり、10以外の数値を取得する確率が高くなります(つまり、生成する数値が多いほど、残りの各数値の確率が高くなります)。

考慮すべきもう1つの点があります。Powerballで1つのゲームをプレイすることで勝つ可能性は、私が理解しているように、1億7500万人に1人です。ただし、誰かが勝つ可能性はそれよりもかなり高くなります。特定の数字が「勝っている」/重複しているというオッズよりも、誰かが「勝っている」(つまり、重複している)確率に興味があります。


すべてのビットが同じまたは他の識別子で生成された他のビットから独立して0または1になる可能性が高い方法で4096ビットの識別子を生成している場合、任意の2つの識別子が一致する確率は観測可能な宇宙の約4.0E81原子のそれぞれに対して異なる識別子をランダムに生成したとしても、非常に小さくなります。ような識別子はほぼ確実に一意であるという事実は、どのような方法でそれらを「非ランダム」することはないだろう
supercat

@supercatそれは本当です-十分に大きな数が与えられた場合、重複がある可能性は非常に低いですが、不可能ではありません。OPが記述していることが良いアイデアであるかどうかは、非一意性の結果がどれほど悪いかによって異なります。
EJoshuaS -復活モニカ

偶然の衝突の確率が、流星の衝突が一意のIDに依存するデバイスを破壊する確率よりも小さい場合、エンジニアリングの観点からは、前者について心配する必要はありません。乱数が独立しないようにする可能性のあるものについて心配する必要がありますが、ランダムな衝突は問題ではありません。
supercat

@supercatあなたはこれを誤解していると思います、誕生日のパラドックスに関する他の答えを見てくださいから4096を取得し、nomadictypeがその長さの数との最終的な衝突の可能性を示したように、実際には驚くほど高い。
EJoshuaS -復活モニカ

衝突がまったく受け入れられない場合、32ビットの数値は少数の集団でも短すぎることは間違いありません。十分に大きい数を使用すると、偶然の衝突の可能性を、ちょうど発生しないと安全に仮定できるポイントまで減らすことができ、多くの場合、他の手段を使用するよりも大きい数を使用する方がよい場合があります後者は通常、システムのクロックがリセットされたり、システムがバックアップからリロードされたりした場合でも、元に戻すこともロールバックすることもできない状態遷移にアクセスする必要があるため、一意性を確保します。
supercat 16

0

使用するビット数は関係ありません。2つの「乱数」が異なることを保証することはできません。代わりに、コンピューターのIPアドレスまたは他のネットワークアドレスのようなものと連続番号を使用することをお勧めします。できればHONKIN 'BIG連続番号-128ビット(明らかに符号なし)は良いスタートのように聞こえますが、256がより良いでしょう。


-1

いいえ、もちろんありません。交換せずにサンプルを使用している場合を除き、複製の可能性があります-たとえわずかであっても。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.