1

これをチェックして、

    List<String> list = new ArrayList<String>();
    for (int i = 0; i < 10000; i++) {
        String value = (""+UUID.randomUUID().getLeastSignificantBits()).substring(3, 20);
        assertFalse(list.contains(value));
        assertTrue(value.length() < 18);
        list.add(value);
    }

このメソッドは魅力のように通過します。また、最上位ビットよりも最下位ビットを取得する方がわずかに優れているという印象があります。最上位ビットでは、一部の情報に対して6ビットが固定されており、最下位ビットではそうではないためです。したがって、平均して、最上位ビットと衝突するには 2^29 個の UUID を生成する必要がありますが、最下位ビットとは 2^32 個の衝突が発生します。参照: SO スレッド私はそれを仮定するのは正しいですか?

ここで、メソッドから取得した最下位ビットの最上位 2 桁を切り刻んでいます。私はそれに部分文字列を使用しています。2桁と符号ビットを切り落としていることに注意してください。これは、衝突を起こすために平均して 2^31 個の UUID を生成する必要があるということではないでしょうか?

正確には、17 桁を超えてはならない一意の識別子を生成しようとしています。Java 型という意味ではなく、整数でなければなりません。私のアプローチはどれくらい信頼できますか?

メタ情報:

実際、私たちはいくつかのレガシー システムと統合しており、17 桁以下の一意の番号を提供する必要があります。彼らはそれをデータベースの一意のキーとして持っていると思います。この場合、シーケンスを使用することもできます。最初にそれを提案しました。しかし、代わりに乱数を考え出すことができれば良いと彼らは私に言ったので、消費者は推測できません.

Java での UUID のタイプ 4 実装に関して私が知る限り、衝突を起こすには平均で 2^61 個の UUID を生成する必要があります。これは、最下位ビットで衝突を取得するには 2^32 を生成し、最上位ビットで衝突を取得するには 2^29 を生成する必要があることを意味するのではないでしょうか? はいの場合、左端の 2 桁を切り刻んだ後、最下位ビットの衝突を得るために平均 2^31 を生成する必要があると仮定するのは正しくありませんか?

私も使用しようとしましSecureRandomたが、それも19桁の長い値を与えています。したがって、私も最初にその数字に切り刻むことになります。以下はそのためのコードです。

    List<String> list = new ArrayList();
    Random random = new SecureRandom();
    for (int i = 0; i < 10000; i++) {
        String value = ""+random.nextLong().substring(2, 19);
        assertFalse(list.contains(value));
        assertTrue(value.length() < 18);
        list.add(value);
    }

私が考えることができる他のオプションは、「yyMMddHHmmssSSS+2-seq-digits」形式で日付を使用することです。しかし、それはプロセッサに大きく依存し、推測可能であると思います。99 ラウンド後にミリ秒単位で変化があったかどうかはよくわからないからです。私はそうするかもしれませんが、それはプロセッサの速度に依存します。ただし、99 の同時リクエストはほとんどありません。

4

3 に答える 3

4

Random または SecureRandom を使用してランダムなビットを生成し、それらを数値に変換することをお勧めします。それはより移植性があるはずです。

数字を切り刻むことについてのあなたのポイントがわかりません。長いサイクルの PRNG からの十分なビット数から 17 (10 進数) の数字を生成すると仮定すると、生成された特定の数字のペアに対して 10**17 分の 1 の確率で衝突が発生するはずです。ソースが良好で、十分なビットを使用している場合、「チョッピング」していることは重要ではありません...

10**171インチで十分かどうかはわかりません。任意の時点で (永続ストアに) いくつの数値が存在するかによって異なります。たとえば、現存する数が 4,400 万ある場合、少なくとも 1 つのペアが衝突する可能性は約 1% です。

Birthday Paradox Calculatorに数値を入力してみてください。

編集:必要なのは、64ビットの疑似乱数を長いサイクル長で提供し、生成できるよりも多くの数の繰り返しがないことを絶対に保証するジェネレーターであると思います。ジェネレーターの状態を保持して再開することも可能でなければなりません。次に、10 進数の 17 桁の「乱数」を取得するには、ジェネレーターから次の値を取得し、範囲内にあるかどうかをテストします0 ... 10**17 - 1。ある場合はそれを使用し、ない場合は繰り返します。

ジェネレーターを正しく管理すれば、システムの存続期間中に繰り返しが発生することはないため、衝突のリスクはありません。ただし、PRNG (真の RNG ではない) を使用し、適切なプロパティを持つ PRNG を選択することが重要です。

私が知る限り、Random クラスはサイクル長が2**48;の PRNG を提供します。つまり、数字が繰り返される前に2**48(メソッドなどを使用して) 数字を取得する必要があります。getLong()OTOH、SecureRandom は、非常に長いサイクル カウントで真のランダムまたは疑似ランダムを提供しますが、各呼び出しで数値を繰り返す可能性はゼロではありませんが小さいです。

于 2009-11-13T06:29:28.600 に答える
1

わかりました、いくつかの質問、私は最善を尽くします

  1. 上位ビットではなく下位ビットに共謀がある場合でも、一意の ID があります。逆の場合も同様です。したがって、結託するには 2^61 の数字が必要です。
  2. 0.5 の確率で、+ 記号が書かれていないため、3 桁を切り刻んでいます。したがって、合計 2^41 の可能な数字があるため、共謀のサンプル サイズは 2^21 です。(10^18 =~ 2^41)
  3. 結果を取得する方法を見てみましょう: Random.getLong() が 2 回呼び出され、ビットの一部 ( PRNGが作成したランダム ビット) を削除します。Random.getLong() または getInt() を呼び出すよりも信頼性が高いとは思えません。

17 桁の数字が必要な場合は、次のようにしてください。

String id = String.valueOf(random.nextLong) % 1000000000000000000L);

MAX_LONG は 9223372036854775807L であるため、[0,23372036854775807] の範囲の数値が出現する可能性がわずかに高くなります。

また、あなたのメソッドとこれの両方が一意のIDを保証しません。

于 2009-11-13T06:27:15.610 に答える
0

UUID アルゴリズムは実装固有です。

GUID をより小さな数に分割しても、同じ一意性の広がりや保証は得られません。保存しているビットは本当に重要ですか?

于 2009-11-13T04:49:39.540 に答える