web-dev-qa-db-ja.com

GUIDが一意ではないことの簡単な証明

GUIDは単純なテストプログラムでは一意ではないことを証明したいと思います。私は次のコードが何時間も実行されることを期待していましたが、うまくいきません。どうやって動かすの?

BigInteger begin = new BigInteger((long)0);
BigInteger end = new BigInteger("340282366920938463463374607431768211456",10);  //2^128
for(begin; begin<end; begin++)
  Console.WriteLine(System.Guid.NewGuid().ToString());

私はC#を使っています。

323
Kai

甲斐、私はあなたがスレッドを使用して欲しいものをするプログラムを提供しました。それは次の条件の下でライセンスされています:あなたはあなたがそれを走らせるCPUコアあたり1時間あたり私に0.0001ドル払わなければなりません。料金は各暦月の終わりに支払われます。あなたの最も早い都合で私のPaypal口座詳細のために私に連絡してください。

using System;
using System.Collections.Generic;
using System.Linq;

namespace GuidCollisionDetector
{
    class Program
    {
        static void Main(string[] args)
        {
            //var reserveSomeRam = new byte[1024 * 1024 * 100];     // This indeed has no effect.

            Console.WriteLine("{0:u} - Building a bigHeapOGuids.", DateTime.Now);
            // Fill up memory with guids.
            var bigHeapOGuids = new HashSet<Guid>();
            try
            {
                do
                {
                    bigHeapOGuids.Add(Guid.NewGuid());
                } while (true);
            }
            catch (OutOfMemoryException)
            {
                // Release the ram we allocated up front.
                // Actually, these are pointless too.
                //GC.KeepAlive(reserveSomeRam);
                //GC.Collect();
            }
            Console.WriteLine("{0:u} - Built bigHeapOGuids, contains {1} of them.", DateTime.Now, bigHeapOGuids.LongCount());


            // Spool up some threads to keep checking if there's a match.
            // Keep running until the heat death of the universe.
            for (long k = 0; k < Int64.MaxValue; k++)
            {
                for (long j = 0; j < Int64.MaxValue; j++)
                {
                    Console.WriteLine("{0:u} - Looking for collisions with {1} thread(s)....", DateTime.Now, Environment.ProcessorCount);
                    System.Threading.Tasks.Parallel.For(0, Int32.MaxValue, (i) =>
                    {
                        if (bigHeapOGuids.Contains(Guid.NewGuid()))
                            throw new ApplicationException("Guids collided! Oh my gosh!");
                    }
                    );
                    Console.WriteLine("{0:u} - That was another {1} attempts without a collision.", DateTime.Now, ((long)Int32.MaxValue) * Environment.ProcessorCount);
                }
            }
            Console.WriteLine("Umm... why hasn't the universe ended yet?");
        }
    }
}

シモンズ:私はパラレルエクステンションライブラリを試してみたかった。それは簡単でした。

そして、制御フローとしてOutOfMemoryExceptionを使用することはただ間違っていると感じます。

EDIT

まあ、これはまだ投票を集めているようです。だから私はGC.KeepAlive()の問題を修正しました。そしてC#4で動くように変更しました。

そして私のサポート条件を明確にするために:サポートは2010年2月28日にのみ利用可能です。その日だけサポートを要請するためにタイムマシンを使ってください。

編集2いつものように、GCは私がメモリを管理するよりも良い仕事をしています。私自身がそれをしようとする以前の試みは失敗する運命にありました。

407
ligos

これは何時間もかかります。それが1GHzでループすると仮定すると(そうではない - それはそれよりずっと遅くなるだろう)、それは10790283070806014188970年間走るだろう。これは宇宙の年齢よりも約830億倍長いです。

Mooresの法則 が成り立つと仮定すると、このプログラムを実行せず、数百年待って数十億回ものコンピュータで実行する方がはるかに速いでしょう。もっと早く。実際、CPU速度が2倍(約18か月)になるよりも実行に時間がかかるプログラムは、CPU速度が上がるまで待ってから実行する前に新しいCPUを購入すると、より早く完了します。新しいハードウェアで一時停止して再開することができます。

226
rjmunro

A GUIDは理論的には一意ではありません。これがあなたの証明です:

  • GUIDは128ビットの数値です
  • 古いGUIDを再利用しないと2 ^ 128 + 1以上のGUIDを生成することはできません。

しかし、太陽の全出力がこのタスクの実行に向けられている場合、それが終了するずっと前に寒くなるでしょう。

GUIDは、いくつかの異なる戦術を使用して生成することができます。そのうちのいくつかは、特定のマシンが同じGUIDを2回生成しないことを保証するための特別な対策を取ります。特定のアルゴリズムで衝突を見つけることは、GUIDを生成するためのあなたの特定の方法が悪いことを示しますが、一般的にGUIDについては何も証明しません。

170
tylerl

もちろん、GUIDは衝突する可能性があります。 GUIDは128ビットなので、それらの2^128 + 1を生成するだけで、 ピジョンホール原理 によって衝突があるはずです。

しかし、GUIDが一意であると言うと、実際には、キースペースが非常に大きいため、誤って同じGUIDを2回生成することは事実上不可能です。ランダムにGUIDを生成しています。

あなたがランダムにnGUIDのシーケンスを生成するならば、少なくとも1つの衝突の確率はおよそp(n) = 1 - exp(-n^2 / 2 * 2^128)(これは可能な数で 誕生日問題 です)誕生日は2^128)です。

   n     p(n)
2^30 1.69e-21
2^40 1.77e-15
2^50 1.86e-10
2^60 1.95e-03

これらの数字を具体的にするために、2^60 = 1.15e+18。したがって、1秒間に10億のGUIDを生成した場合、2^60のランダムGUIDを生成するには36年かかりますが、それでも衝突が発生する可能性は1.95e-03です。あなたはあなたの人生のある時点で 殺害される可能性が高いです4.76e-03)あなたは次の36年間で衝突を見つけることよりも。がんばろう。

137
jason

独自性が心配な場合は、いつでも新しいGUIDを購入できるので、古いGUIDを捨てることができます。あなたが望むならば私はeBayにいくらかを置くつもりです。

61
ctacke

個人的には、「ビッグバン」は2つのGUIDが衝突したときに発生したと思います。

47
AMissico

あなたはそれをO(1) timeで 量子ボゴソート アルゴリズムの変形を使って示すことができます。

Guid g1 = Guid.NewGuid();
Guid g2 = Guid.NewGuid();
if(g1 != g2) Universe.Current.Destroy();
42

2つのGUIDはいずれも一意である可能性が非常に高い(等しくない)。

this SO entry 、および Wikipedia を参照してください。

生成されたそれぞれのGUIDは一意であることが保証されていませんが、一意のキーの総数(2 ^ 128または3.4×10 ^ 38)は非常に大きいため、同じ数が2回生成される確率は非常に小さいです。 。たとえば、約5×10 ^ 22個の星を含む観測可能な宇宙を考えてみましょう。すべての星は6.8×10 ^ 15個の普遍的に一意のGUIDを持つことができます。

ですから、おそらくあなたは何十億年もの間待たなければならないでしょう。

28
Graviton

[更新:]以下のコメントが示すように、新しいMS GUIDはV4であり、GUIDの一部としてMACアドレスを使用しません。 _世代(私はMSからのV5実装の兆候を見たことがないので、誰かにリンクを張ってもらえれば私に知らせてください)。とはいえ、V4では、時間は依然として要因であり、GUIDの重複に対するオッズは非常に小さいままであるため、実際の使用には無関係です。あなたは確かにOPがやろうとしていたようなただ一つのシステムテストから重複するGUIDを生成することはありそうもないでしょう。

これらの答えのほとんどは、MicrosoftのGUID実装に関する重要なポイントを1つ欠けています。 GUIDの最初の部分はタイムスタンプに基づいており、別の部分はネットワークカードのMACアドレス(またはNICがインストールされていない場合は乱数)に基づいています。

これが正しく理解できれば、GUIDを複製する唯一の信頼性の高い方法は、MACアドレスが同じでクロックが同じ複数のマシンで同時にGUID世代を実行することであることを意味します。両方のシステムで世代が発生したときに正確に同じ時間でした(タイムスタンプは私がそれを正しく理解すればミリ秒に基づいています)....それでもランダムである数に他のビットがたくさんあるので、オッズはまだ消えて小さいです。

すべての実用的な目的のために、GUIDは普遍的に一意です。

MS GUID overのかなり良い説明が "The Old New Thing"ブログ にあります

27
Stephen M. Redd

これは、コードの多くの場所でguidの一意性をチェックしたい場合に使用できる、気の利いた小さな拡張方法です。

internal static class GuidExt
{
    public static bool IsUnique(this Guid guid)
    {
        while (guid != Guid.NewGuid())
        { }
        return false;
    }
}

それを呼び出すには、新しいguidを生成するたびにGuid.IsUniqueを呼び出すだけです。

Guid g = Guid.NewGuid();
if (!g.IsUnique())
{
    throw new GuidIsNotUniqueException();
}

……最初のラウンドでうまくいったことを確認するために、2回呼び出すことをお勧めします。

23
KristoferA

2 ^ 128まで数えます - 野心的です。

1秒間に2 ^ 32のIDを数えることができると想像してみてください - thatambitiousではありません。これは1秒間に43億でさえないためです。その作業に2 32台のマシンを捧げましょう。さらに、それぞれに2 ^ 32の文明を与えて、同じリソースをタスクに割り当てます。

これまでのところ、1秒間に2 ^ 96のIDを数えることができます。つまり、2 ^ 32秒間(136年強)カウントされます。

今、私たちが必要としているのは、4,294,967,296の文明をそれぞれの専用の機械に4,294,967,296台のマシンに割り当てればよいだけです。 - )

19
Steve314

830億年の実行時間があなたを怖がらないのであれば、複製があるかどうかをチェックするために生成されたGUIDをどこかに保存する必要もあると思います。 2 ^ 128の16バイト数を格納するには、4951760157141521099596496896テラバイトのRAM前払いで割り当てる必要があるだけなので、それに合うコンピュータがあり、10バイトのテラバイトDIMMを購入する場所を見つけることができます。あなたが「実行」を押す前に、あなたは真剣に現在の軌道からそれをずらすことができます。考え直してください!

17
kibitzer
for(begin; begin<end; begin)
    Console.WriteLine(System.Guid.NewGuid().ToString());

beginをインクリメントしていないので、条件begin < endは常にtrueです。

12
Nathan Taylor

GUIDの衝突が懸念される場合は、代わりに ScottGuID を使用することをお勧めします。

11
Matt Peterson

しかし、あなたはあなたが複製を持っていることを確かめなければならない、あるいはが複製であることができる場合にだけあなたは気にしなければならないか。同じ誕生日を持つ2人の人がいることを確認するには、366人が必要です(うるう年は含まない)。同じ誕生日に2人の人がいる可能性が50%を超えるためには、23人だけが必要です。それが 誕生日の問題です

32ビットの場合、50%以上の重複の可能性があるために必要なのは77,163個の値だけです。やってみよう:

Random baseRandom = new Random(0);

int DuplicateIntegerTest(int interations)
{
    Random r = new Random(baseRandom.Next());
    int[] ints = new int[interations];
    for (int i = 0; i < ints.Length; i++)
    {
        ints[i] = r.Next();
    }
    Array.Sort(ints);
    for (int i = 1; i < ints.Length; i++)
    {
        if (ints[i] == ints[i - 1])
            return 1;
    }
    return 0;
}

void DoTest()
{
    baseRandom = new Random(0);
    int count = 0;
    int duplicates = 0;
    for (int i = 0; i < 1000; i++)
    {
        count++;
        duplicates += DuplicateIntegerTest(77163);
    }
    Console.WriteLine("{0} iterations had {1} with duplicates", count, duplicates);
}

1000 iterations had 737 with duplicates

今128ビットがたくさんあるので、あなたはまだあなたにまだ衝突の可能性が低い多くのアイテムを話しています。近似を使用すると、与えられたオッズに対して次の数のレコードが必要になります。

  • 1/1000の確率で衝突が起こる8億ドル
  • 衝突が50%発生する可能性がある217億億
  • 90%の確率で衝突が起こる396億

1年に約1E14通の電子メールが送信されるので、同じGUIDを2回使用する確率は90%になる前にこのレベルで約400,000年になりますが、これは830億台のコンピュータを実行する必要があるというのとは大きく異なります。それは、宇宙の年齢の倍、または、太陽が複製を見つける前に寒くなるだろうということです。

9
Jason Goemaat

おそらくあなたは、Guidsを生成するためのアルゴリズムが真に乱数を生成するのではなく、実際には周期<< 2 ^ 128で循環していると信じる理由を持っています。

例えば一部のビットの値を固定するGUIDを導出するために使用されていたRFC4122メソッド。

サイクリングの証明は、可能な期間の長さによって異なります。

小さな期間では、GUIDが一致しない(衝突した場合は終了する)場合は衝突時に置き換えられるハッシュテーブル(hash-of-hash(GUID) - > GUID)がアプローチになります。また、交換をランダムな時間だけ実行することも検討してください。

結局、衝突間の最大期間が十分に大きい場合(そして事前に知られていない場合)、どの方法でも衝突が存在する場合に衝突が発見される可能性が生じるだけです。

Guidsを生成する方法がクロックベースの場合(RFCを参照)、衝突が存在するかどうかを判断できない可能性があることに注意してください。または(b)あなたは衝突を強要するのに十分なGuidを時計目盛り内に要求することができない。

あるいは、Guid内のビット間の統計的な関係、またはGuid間のビットの相関を示すことができます。そのような関係は、必ずしも実際の衝突を見つけることができずにアルゴリズムに欠陥がある可能性が高い可能性があります。

もちろん、Guidsが衝突する可能性があることを証明したいだけであれば、プログラムではなく数学的証明がその答えです。

9
MZB

私は誰もあなたのグラフィックスカードのアップグレードについて言及していない理由を理解していません...あなたがハイエンドのNVIDIA Quadro FX 4800か何か(192 CUDAコア)を手に入れたなら確かにこれは速くなるでしょう...

もちろん、NVIDIA Qadro Plex 2200 S4(960 CUコア)を購入することができれば、この計算は本当に叫ぶことになります。おそらく、NVIDIAはPRのスタントとして「テクノロジーデモンストレーション」のためにあなたにいくつかを貸しても構わないと思っていますか?

きっと彼らはこの歴史的計算の一部になりたいのですが….

8
Dad

皆さん全員が大きなポイントを逃していませんか?

GUIDは、グローバルに一意である可能性が非常に高い2つのことを使用して生成されたと思いました。 1つはあなたがいるマシンのMACアドレスでシードされ、2つはそれらが生成された時間に乱数を加えたものを使います。

したがって、実際のマシンで実行し、マシンがGUIDで時間を表すために使用する最小の時間内にすべての推測を実行しない限り、推測の数に関係なく、同じ数値は生成されません。システムコールを使用してください。

GUIDの実際の作成方法を知っていれば、実際に推測する時間が大幅に短縮されると思います。

トニー

7
AnthonyLambert

あなたはGUIDをハッシュすることができます。そうすれば、あなたはずっと早く結果を得られるはずです。

もちろん、同時に複数のスレッドを実行することも賢明です。そうすることで、競合状態が異なるスレッドで同じGUIDを2回生成する可能性が高まります。

7
Michael Stum
  1. ニューヨーク市の低温研究所に行きます。
  2. 1990年の間(大体)凍結してください。
  3. Planet Expressで仕事を始めましょう。
  4. 真新しいCPUを買いなさい。コンピュータを構築し、プログラムを実行し、最後の審判のような疑似永久モーションマシンで安全な場所に置きます。
  5. タイムマシンが発明されるまで待ってください。
  6. タイムマシンを使って未来へジャンプ。 1YHz 128bit CPUを購入した場合は、プログラムの実行開始後に3,938,453,320 days 20 hours 15 minutes 38 seconds 463 ms 463 μs 374 ns 607 psに移動してください。
  7. …?
  8. 利益!

...あなたが1GHzのCPUより10,783,127(またはバイナリプレフィックスを使うことを好むのであれば1,000,000,000,000,000)倍の1YHz CPUを持っていても少なくとも1,125,899,906,842,624年かかります。

それで、計算が終わるのを待つよりも、他のnハトが彼らの家を取ったので彼らの家を失ったハトを養うほうが良いでしょう。 :(

あるいは、128ビットの量子コンピュータが発明されるまで待つことができます。それから、あなたは妥当な時間内にあなたのプログラムを使うことによって(おそらく)GUIDがユニークではないことを証明するかもしれません。

7
JiminP

4ビットがバージョン番号を保持するため、GUIDは124ビットです。

6
Behrooz

Begin ++の代わりにbegin = begin + new BigInteger((long)1)を試しましたか?

4
RCIX

生成されているUUIDの数がムーアの法則に従っている場合、近い将来GUIDを使い果たすことはないという印象は偽です。

2 ^ 128のUUIDを使用すると、すべてのUUIDがなくなるまでに18か月* Log2(2 ^ 128)〜= 192年かかります。

そして、UUIDが大量に採用されてからの過去数年間で、(統計的な証明なしに)、私たちがUUIDを生成しているスピードは、ムーアの法則が示すよりもずっと速くなっていると思います。言い換えれば、UUID危機に対処しなければならなくなるまでには、おそらく192年もかからないでしょう。それは宇宙の終わりよりもずっと早い時期です。

しかし、2012年末までには確実に実行できなくなるので、問題を心配するために他の種に任せます。

4
Bill Yang

ここで焚き火をしないでください、しかしそれは実際に起こります、そして、はい、私はあなたがこの男を与えてきた冗談を理解します、しかしGUIDは原則的に唯一のユニークです、私はこれにぶつかりましたWP7エミュレータにはバグがあり、起動するたびに最初に呼び出されたときにSAME GUIDが表示されるという意味です。したがって、理論的には競合が発生しない場合、上記のGUIの生成に問題があれば、重複する可能性があります。

http://forums.create.msdn.com/forums/p/92086/597310.aspx#597310

3
Ben

GUID生成コード内のバグのオッズは、衝突が生成されるアルゴリズムのオッズよりはるかに高いです。 GUIDをテストするためのコードのバグの可能性はさらに大きいです。あきらめる。

3
Mark Ransom

プログラムは、そのエラーにもかかわらず、GUIDが一意ではないことを証明しています。反対を立証しようとする者はその点を見逃している。このステートメントは、GUIDのいくつかのバリエーションの弱い実装を証明しているだけです。

GUIDは定義上一意である必要はなく、定義上非常に一意です。あなたはただ高度の意味を洗練しました。バージョン、実装者(MSまたは他の人)、VMの使用などに応じて、大幅に変更された定義があります。 (以前の記事のリンクを参照)

あなたはあなたのポイントを証明するためにあなたの128ビットテーブルを短くすることができます。最善の解決策は、重複を含むテーブルを短くするためにハッシュ式を使用し、ハッシュが衝突したらそれに基づいてGUIDを再生成した後に完全な値を使用することです。別の場所から実行している場合は、ハッシュとフルキーのペアを中央の場所に保存します。

Ps:目標がx個の異なる値を生成することだけである場合は、この幅のハッシュテーブルを作成し、そのハッシュ値を確認してください。

2
ydebilloez

Guid生成の一部は現在のマシンの時間に基づいているため、重複したGuidを取得するための私の理論は次のとおりです。

  1. Windowsのクリーンインストールを実行する
  2. Windowsの起動時と同じように、時間を2010-01-01 12:00:00にリセットする起動スクリプトを作成します。
  3. 起動スクリプトの直後に、アプリケーションがGuidを生成します。
  4. このWindowsインストールのクローンを作成して、以降の起動時に発生する可能性のあるわずかな違いを除外します。
  5. このイメージでハードドライブを再イメージ化して、マシンを数回起動します。
1
realworldcoder

私にとっては..一つのコアがUUIDv1を生成するのにかかる時間はそれがユニークであることを保証します。マルチコアの状況でも、UUIDジェネレータが特定のリソースに対して一度に1つのUUIDしか生成できない場合は、リソースがアドレスの一部であるため、複数のリソースが同じUUIDを完全に利用することはできません。タイムスタンプが切れるまであなたを長持ちさせるのに十分な数のUUIDがあります。どの時点で私はあなたが気になるだろうと本当に疑います。

0
whardier

これも解決策です。

int main()
{
  QUuid uuid;
  while ( (uuid = QUuid::createUuid()) != QUuid::createUuid() ) { }
  std::cout << "Aha! I've found one! " << qPrintable( uuid.toString() ) << std::endl;
}

注意:Qtが必要ですが、十分に長く実行させればそれが見つかるかもしれません。

(注:実際には、今見ているので、生成された2つのuuidが衝突しないようにするための生成アルゴリズムについて何かあるかもしれませんが、それは疑いの余地があります)。

0
Scott

GUIDが一意ではないことを証明する唯一の解決策は、World GUID Poolを持つことです。 GUIDがどこかで生成されるたびに、それは組織に登録されるべきです。あるいは、すべてのGUIDジェネレータが自動的に登録する必要があり、そのためにはアクティブなインターネット接続が必要であるという標準化を含める必要があります。

0
nawfal