ネットワークプロトコルを実装しており、パケットに一意の識別子が必要です。これまでのところ、ランダムな32ビット整数を生成しており、プログラム/接続の存続期間中に衝突が発生する可能性は天文学的に低いと想定しています。これは一般に、本番コードで受け入れられるプラクティスと考えられていますか、それとも衝突を防ぐためにより複雑なシステムを考案する必要がありますか?
ネットワークプロトコルを実装しており、パケットに一意の識別子が必要です。これまでのところ、ランダムな32ビット整数を生成しており、プログラム/接続の存続期間中に衝突が発生する可能性は天文学的に低いと想定しています。これは一般に、本番コードで受け入れられるプラクティスと考えられていますか、それとも衝突を防ぐためにより複雑なシステムを考案する必要がありますか?
回答:
誕生日のパラドックスに注意してください。
サイズNのセット(ランダムな場合はN = 2 ^ 32)から一連のランダムな値を(一様に、独立して)生成するとします。
次に、誕生日のパラドックスの経験則では、sqrt(N)値について生成すると、少なくとも50%の確率で衝突が発生する、つまり、少なくとも2つの同一の値が生成されたシーケンス。
N = 2 ^ 32、sqrt(N)= 2 ^ 16 = 65536の場合、約65kの識別子を生成した後、2つの識別子が衝突する可能性が高くなります。1秒あたりの識別子を生成する場合、これは1日以内に発生します。言うまでもなく、多くのネットワークプロトコルはそれよりずっと高速に動作します。
乱数に十分なビットがある場合、一意である乱数に依存することは広く受け入れられると考えられています。乱数を繰り返すとセキュリティ全体が破壊される暗号化プロトコルがあります。使用されている乱数ジェネレーターに深刻な脆弱性がない限り、それは問題ではありません。
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は、同じサイズを目指して、十分な安全マージンで一意性を確保する必要があります。
sqrt(2^122)
= 2.3兆の兆のUUIDの
urandom
、UUIDライブラリを使用するよりも多くの作業ではありません。比較のために両方をPythonで実装したところ、各メソッドは正確に25文字のソースコードでした。
私はこれを悪い習慣と呼びます。乱数生成では、単に一意の番号を作成するのではなく、単に乱数を作成します。ランダムな分布には、いくつかの重複が含まれる可能性があります。時間の要素を追加することで、この状況を受け入れ難くすることができます。システムクロックからミリ秒単位で現在の時刻を取得する場合。このようなもの:
parseToInt(toString(System.currentTimeMillis()) + toString(Random.makeInt()))
長い道のりを歩みます。一意性を本当に保証するには、明らかにUUID / GUIDを使用する必要があります。しかし、それらは生成するのに費用がかかる可能性があります。重複の唯一の可能性は、ランダム生成が同じミリ秒で重複していた場合であるため、上記はおそらく十分です。
currentTimeMillis
ラップアラウンドするまでは0%の衝突の可能性があります。
System.currentTimeMillis
と含有するものをRandom.makeInt()
、そして衝突の確率は、実質的にダウンしました。ただし、この例のコードはそれを行いません。与えられた任意の前回とランダム値、および任意の現在の時刻、衝突の確率は、最初の場所で衝突2個の乱数の確率と同じです。
確かに、2つのランダムな32ビット整数が連続している可能性はかなり低いですが、完全に不可能ではありません。適切なエンジニアリングの決定は、衝突の結果、発生する数値の推定、一意性が必要とされる寿命、および悪意のあるユーザーが衝突を試み始めた場合に何が起こるかに基づいています。
乱数は一意であると仮定することは許容できますが、注意する必要があります。
あなたの乱数が均等に分散されていると仮定すると、衝突の確率はおおよそ(nは2 /2)/ Kここで、nあなたが生成する乱数の数であり、kは、「ランダム」数が取ることができる可能な値の数です。
あなたは天文学的に考えられないほど数字をつけないので、それを2 30分の 1 (およそ10億で)と考えてみましょう。さらに、2 30個のパケットを生成するとします(各パケットが約1キロバイトのデータを表す場合、これは約1テラバイトの合計データを意味しますが、想像できないほど大きくはありません)。少なくとも2 89の可能な値を持つ乱数が必要であることがわかりました。
まず、乱数は十分に大きくする必要があります。32ビットの乱数は、最大2 32の可能な値を持つことができます。十分な高さのないビジーなサーバーの場合。
次に、乱数ジェネレーターの内部状態を十分に大きくする必要があります。乱数ジェネレーターの内部状態が32ビットのみの場合、そこから生成する値の大きさに関係なく、可能な最大値は2 32個になります。
第三に、接続内だけでなく接続全体で乱数を一意にする必要がある場合は、乱数ジェネレーターを適切にシードする必要があります。これは、プログラムが頻繁に再起動される場合に特に当てはまります。
一般に、プログラミング言語の「通常の」乱数ジェネレーターは、このような使用には適していません。暗号化ライブラリによって提供される乱数ジェネレーターは、一般的にあります。
多くの人がすでに高品質の答えを出していますが、いくつかの小さな点を付け加えたいと思います。まず、誕生日のパラドックスに関する@nomadictypeの点は素晴らしいです。
もう1つのポイント:ランダム性は、人々が通常想定するほど生成や定義が簡単ではありません。(実際、利用可能なランダム性の統計的検定が実際にあります)。
そうは言っても、ギャンブラーの誤、に注意することは重要です。ギャンブラーの誤acyは、独立したイベントが何らかの形で互いに影響し合うと人々が推測する統計上の誤acyです。ランダムイベントは通常、統計的に互いに独立しています。つまり、「10」をランダムに生成した場合、少なくとも「10」を生成する将来の確率は変わりません。(たぶん誰かがその規則の例外を思いつくかもしれませんが、私はそれがほとんどすべての乱数ジェネレーターの場合に当てはまると期待しています)。
したがって、私の答えは、十分に長い乱数のシーケンスが一意であると仮定できる場合、それは明確な統計パターンになるため、実際には乱数ではないということです。また、たとえば、10を生成すると、将来の10を生成する確率が0%(発生する可能性は低い)になるため、新しい番号はそれぞれ独立したイベントではないことを意味します。つまり、10以外の数値を取得する確率が高くなります(つまり、生成する数値が多いほど、残りの各数値の確率が高くなります)。
考慮すべきもう1つの点があります。Powerballで1つのゲームをプレイすることで勝つ可能性は、私が理解しているように、1億7500万人に1人です。ただし、誰かが勝つ可能性はそれよりもかなり高くなります。特定の数字が「勝っている」/重複しているというオッズよりも、誰かが「勝っている」(つまり、重複している)確率に興味があります。