天文学的な確率はゼロに近いが NOT ゼロ
안녕하신게라!パナソニック コネクト株式会社クラウドソリューション部の加賀です。
以前、「うちのサービス、バズったらどうなる?」計算量を味方につける、エンジニアのためのパフォーマンス入門で、性能を感覚ではなく桁数で語る話を書きました。その姉妹編にあたります。
設計レビューで「主キーはUUIDでいいですか? 1万件ならほぼ衝突しませんよね?」と聞かれ、「宝くじに当たるより低いですわ」と答えてお茶を濁したことがあります。納得した顔をされ、議題は次へ進みました。
我ながら、ひどい答えです。宝くじの当選確率も、UUIDの衝突確率も、桁数を確かめないまま口にしていたのですから。で、実際どんだけ低いんでしょうか。計算しました。
1万個のUUIDv4が1組でも衝突する確率は、天文学的に低い3.98NJTです。
低いという回答の方向性は合っていましたが、想像以上にとてつもなく低かった。反省です。
にもかかわらず、リリース後「UUIDが重複した」という報告が何度か届きます。マ!?
3.98NJTを何度も引くのなら、毎年宝くじ買ってた方が良かったのでしょうか?
いいえ、何かカラクリがあるはずです。
今回扱うのは、しこたま小さい確率の桁数です。
なるべく難しい数学の話はしませんが、ま、ちょっと覚悟はしておけ。
だから、NJTって何なのよ?
たった1枚の年末ジャンボで1等が当たる夢のドリームな確率 0.000005%1 です。
NJTは「(N)年末 (J)ジャンボ (T)当選」の頭文字から捏造した単位で、x年連続で1等を当て続ける確率を x NJT と表記します。数字が大きいほどあり得ない確率になります。式もシンプル。
$$
x\ \mathrm{NJT} = (5 \times 10^{-8})^{x}
$$
年数が整数である必要はなく、対数で計算するとxに3.98のような半端な値も出ます。記事のタイトルで出した 3.98NJT は、ほぼ4年連続で1等に当選する確率です。と言うと、そのあり得なさが伝わりますでしょうか。1回でも超豪運なのに。
UUIDv4の衝突確率の計算式は後回しにします。計算結果とNJTの温度感を見てください。
UUIDv4の衝突確率は、何NJT相当か
UUIDv4をつくる個数が少ない順に並べました。下へ行くほど、衝突は現実味を帯びていきます。
| UUIDv4をこれだけつくると | 衝突(当選)する確率 | NJT | 年末ジャンボ1枚 |
|---|---|---|---|
| 2個 | $1.9 \times 10^{-37}$ | 5.03 | |
| 2~3個の間(2.4個) | $3.1 \times 10^{-37}$ | 5.00 | 1等に5年連続 |
| 100個 | $9.4 \times 10^{-34}$ | 4.52 | |
| 8,152個 | $6.25 \times 10^{-30}$ | 4.00 | 1等に4年連続 |
| 1万個 | $9.4 \times 10^{-30}$ | 3.98 | |
| 約3,646万個 | $1.25 \times 10^{-22}$ | 3.00 | 1等に3年連続 |
| 1億個 | $9.4 \times 10^{-22}$ | 2.88 | |
| 約1,630億個 | $2.5 \times 10^{-15}$ | 2.00 | 1等に2年連続 |
| 1兆個 | $9.4 \times 10^{-14}$ | 1.78 | |
| 約729兆個 | $5 \times 10^{-8}$ | 1.00 | 1等が当たる |
| 約1.03京個 | $9.95 \times 10^{-6}$ | 0.69 | 1等の組違い賞が当たる |
| 約106京個 | $1 \times 10^{-1}$ (10%) | 0.14 | 末等が当たる |
| 約271京個 | $5 \times 10^{-1}$ (50%) | 0.04 |
流石に271京個も作ると50%で衝突します。ただしこの数、毎秒6個のUUIDv4を138億年ほど作り続けると到達する量です。宇宙の年齢ぶん回してやっとコイントスです。つまり、数学的には安心して構いません。(宇宙誕生から稼働しているアプリケーションを運用されている方が居らしたらごめんなさいをしたいので会わせてください)
逆に、たった2個でも5.03NJTで、絶対に 0% になりません。確率の律儀なところです。
【コラム】捏造した単位は、対数目盛り
思いつきで捏造した単位 NJT は、使ってみるとあり得なさを測る指標として便利でした。
UUIDv4が2個で5.03NJTだ!1万個で3.98NJTだ!と言い切れる使い勝手の良さよ。
ただし、日常で出会う確率にはまるで使えません。(対数慣れしている方を除く)
なぜなら0NJTから1NJTまでの間に、「必ず起きる」から「宝くじ1等」までが全部詰まっているためです。50%が 0.04NJT、10%が 0.14NJT、1%が 0.27NJT。0.001%で 0.68NJT。
あれ?1等って簡単に当たるの?と思えてしまい、1NJT未満は直感に反する値になります。
ただし、この表がまるごと成立するのは $N = 2^{122}$ のときだけです。
なぜなのか、後回しにしたUUIDv4の衝突確率の計算式について。
「これから作るUUIDの1個が、既存のどれかとぶつかる確率」ではなく「発行済みのUUID同士がぶつかる確率」です。比較の回数は $n-1$ ではなく $n(n-1)/2$ 組。誕生日のパラドックスと同じく、UUID数に対して2乗でぶつかりやすくなっていきます。
計算を簡易化するために近似式を用います2。$N$ 通りの値から $n$ 個を取り出したとき、少なくとも1組が重複する確率 $p$ は、
$$
p(n) \approx 1 - e^{-\frac{n^2}{2N}}
$$
となります。$N$ はUUIDv4が取りうる値の総数です。
UUIDは全部で128ビットですが、その中にバージョンを示す4ビットとバリアント(仕様の系統)を示す2ビットが予約されているので、v4で本当にランダムなのは122ビットしかありません。
その122ビットで表される総数 $N$ は $2^{122}$ 、約 $5.32 \times 10^{36}$ 個です。
「5.32兆個の1兆倍の、さらに1兆倍ですよ」と言われても桁数が多すぎてピンときません。
記事中ずっとゼロを36個とか言われてもチンプンカンプンなので、確率の薄さが実感しやすい「宝くじ」で測ることにした次第です。
NJTを決めているのは、ビット数
答えから書くと、122ビットを全部使うのはUUIDv4だけです。UUIDの仕様 RFC 95623 はバージョンごとに128ビットの使い道を決めていて、1個作るたびに変わる場所も違います。まずは、どこが変わるのか。
凡例 R:乱数 T:時刻 C:clock_seq M:MACアドレス H:ハッシュ v:版 n:バリアント
v4 RRRRRRRR-RRRR-vRRR-nRRR-RRRRRRRRRRRR
v7 TTTTTTTT-TTTT-vRRR-nRRR-RRRRRRRRRRRR
v6 TTTTTTTT-TTTT-vTTT-nRRR-RRRRRRRRRRRR
v1 TTTTTTTT-TTTT-vTTT-nCCC-MMMMMMMMMMMM
v3/v5 HHHHHHHH-HHHH-vHHH-nHHH-HHHHHHHHHHHH
v の4ビットと n の上位2ビットが、128から引かれる6ビットの正体です(n の下2ビットには各バージョンのデータが相乗りするので、この桁だけ半分変わります)。あとは変わる文字を数えるだけ。1文字4ビット。
| バージョン | 中身 | UUIDを1個作るたびに ランダムに使われるビット数 |
|---|---|---|
| v4 | 完全ランダム | 122ビット |
| v7 | Unixミリ秒48ビット+ランダム | 74ビット(カウンタ併用で減る) |
| v6 | Gregorian時刻+clock_seq+node | 62ビット(MAC方式を選ぶと0) |
| v1 | v6と同じ部品、並びが違うだけ | 実質0ビット(MAC方式) |
| v3 / v5 | 名前空間ID+名前のMD5 / SHA-1ハッシュ | 0ビット |
| v8 | 実装が自由に決める | 不定 |
図のv1とv6、使っている部品は同じです。 60ビットの時刻に、14ビットのclock_seq、48ビットのnode。違うのは並び順だけ。なのに0と62に分かれます。v1はnodeがMACアドレスで、clock_seqも「システムの生涯で一度だけ」しか引き直しません。UUIDごとに作られる乱数を持っていないわけです。v6は同じ箱を「新しいUUIDごとに疑似乱数へリセット(SHOULD)」しますが、旧来のMAC方式もMAYで残っているので、v6なら安全、とは言えません。
v3とv5は乱数ですらなく、同じ名前空間へ同じ名前を入れれば必ず同じ値が返ります。v8は122ビットの箱を実装が好きに使えるだけで、RFCも「一意性は実装依存であり、仮定してはならない(MUST NOT be assumed)」と名指し。箱はありますが、担保はありません。
NJTの議論は、常に「そのバージョンで何ビットが乱数か」の上に乗っています。 冒頭の3.98NJTも、全122ビットが乱数である前提の数字でした。前提が変われば、桁も変わります。
しかもこの2つ、単純な比例です。$1\ \mathrm{bit} = \log_{5 \times 10^{-8}} 2 \approx 0.0412\ \mathrm{NJT}$、ひっくり返せば 1NJT ≒ 24.25ビット。122ビットに掛けると $122 \times 0.0412 \approx 5.03$。表の最上行「2個で5.03NJT」と同じ値です(誤差が目立つので比例は1NJT以上推奨)。
【コラム】v7を選ぶ理由は、衝突耐性ではない
74ビットはv4の122ビットより少ないわけですが、確率の上では問題ありません。先頭48ビットがUnixエポックからのミリ秒なので、ぶつかり得るのは同じ1ミリ秒に生成されたUUID同士だけ。$N = 2^{74}$ なら50%に届くのは1ミリ秒あたり約1,600億個です。毎ミリ秒1万個(毎秒1,000万個)という無茶なペースで回しても、1ミリ秒あたり2.00NJT($2.6 \times 10^{-15}$)。1等の2年連続当選です。
v7を選ぶ理由は別のところにあります。値が生成順に並ぶので、主キーにすれば新しい行が常に右端へ積まれます。挿入先が毎回バラバラなv4と違って、インデックスのページ分割が積み上がりません。私は内部の主キーはv7、外部に出すIDはv4にしています。URLやAPIレスポンスに載せたIDから作成時刻を読まれると困るからで、これも衝突耐性とは関係のない理由です。
ところで、v7の「74ビット」には但し書きがあります。上限であって、実測値ではありません。
【コラム】削った12ビットは、どこへ行ったのか
74ビットは上限です。RFC 9562は、同一ミリ秒内の単調増加のために、ランダム部の上位をカウンタ(指針は12〜42ビット)かサブミリ秒のタイムスタンプ(最大12ビット)に充ててもよい(MAY)としています。実際、PostgreSQL 18 の uuidv7() は後者で、先頭12ビットがサブミリ秒に化けるので変動する乱数は62ビット。$N = 2^{62}$ なら前コラムと同じ毎ミリ秒1万個で 1.50NJT。さっきの2.00NJTから0.5ぶん痩せた計算です。
ところがこれ、1万個が全部おなじサブミリ秒に詰まった最悪ケースの値です。実際はその12ビットが生成を4,096スロットへ振り分け、同じスロット同士しかぶつかりません。$4{,}096 \times 2^{62} = 2^{74}$ ですから、実効は74ビットで見積もったのと同じ 2.00NJT。削った12ビットは消えたのではなく、乱数から時刻へ役割が移っただけでした。カウンタでも同じことが起きます。数えるべきはビット数ですが、そのビットが乱数なのか時刻なのかまで見ないと桁を読み違えます。
先頭8文字で切ると、10万個で3.70NJTが一気に0.02NJT
バージョンの選択でビットが減るのは、UUIDの仕様の話でした。ここからは実装の話です。
先頭8文字だけに削っちゃう理由は色々あると思います。長いから。URLに載せると読みにくいから。ログの列幅に収まらないから。管理画面の一覧に並べたいから。そうして短縮表示していたら、いつの間にかその8文字で突合が始まっていた。なんやかんやあって「ランダムだし先頭8文字くらい使えば十分でしょう」と決まってしまう。
いいえ、十分ではありません。 8文字に切るというのは、$2^{122}$ の空間を $2^{32}$ へ、つまり $2^{90}$ 分の1へ潰す操作です。27桁ぶんの余裕を、読みやすさと引き換えに捨てている。その瞬間から、それはもうUUIDではなく、たまたまUUIDから採った32ビットの乱数でしかありません。
何が起きるのか。
動かしたほうが早いです。UUIDを10万個作り、先頭8文字を取り出し、重複を数えてみます。
import uuid, collections
n = 100000
short = [str(uuid.uuid4())[:8] for _ in range(n)] # 先頭8文字だけ使う
dup = [k for k, v in collections.Counter(short).items() if v > 1]
print("生成数:", n)
print("ユニーク数:", len(set(short)))
print("重複した値:", dup)
生成数: 100000
ユニーク数: 99998
重複した値: ['2cf56f6e', '40beea6c']
1秒足らずで、2組が衝突しました。 天文学的確率とはなんだったのか。
理論上の期待値は $n^2/2N = 1.16$ 組なので、計算どおりではあります。さきほどまで宇宙の年齢の話をしていたはずが、8文字に切った瞬間、手元のノートPCで再現できる規模に変わりました。凄まじい爆縮。ノートPCで小宇宙が燃えたのか。
ビット数ごとに整理すると、こうなります。
| 使うビット数 | 16進の文字数 | 衝突確率が50%になる個数 | 10万個での衝突確率 (サンプルコード) |
|---|---|---|---|
| 32ビット | 8文字 | 約7.7万個 | 約69%(0.02NJT) |
| 48ビット | 12文字 | 約1,975万個 | 約0.0018%(0.65NJT) |
| 64ビット | 16文字 | 約50億個 | $2.7 \times 10^{-10}$(1.31NJT) |
| 80ビット | 20文字 | 約1.3兆個 | $4.1 \times 10^{-15}$(1.97NJT) |
| 122ビット | 32文字(全て) ※128ビットのうち6ビットは 版とバリアントで固定 |
約271京個 | $9.4 \times 10^{-28}$(3.70NJT) |
先頭8文字だけを使うと、7.7万個で衝突確率が50%に達します。271京個と7.7万個。その差は、文字を24個削っただけで生まれます。
右端の列は、サンプルコードで走らせた場合の値です。同じ10万個でも、フルの122ビットなら $9.4 \times 10^{-28}$、32ビットに切れば約69%。あり得なさは3.70NJTから0.02NJTへ落ち、確率のほうは27桁ぶん跳ね上がりました。
10万件のテーブルを8文字に切ってキーにしてると、平均して1組は衝突があることになります。
RFC 9562には「Opacity」という節があり、UUIDは可能な限り不透明なもの(中身を解釈しないもの)として扱い、必要がなければパースするなと書かれています。切り落とすのは、パースよりさらに踏み込んだ行為です。「8文字」は短さの単位であって、安全性の単位ではありません。
【コラム】Gitの7文字は、なぜ許される?
「あれ?Gitのコミットハッシュ、7文字だったような?」はい、Yes。
7文字は表示上の省略形であり、複数のオブジェクトに当たれば ambiguous として停止します。内部はフル桁で処理しています。
しかもGitは表示する桁数を、オブジェクト数に合わせて自動で伸ばしています。 Git 2.11のリリースノート曰く「既定の短縮長は歴史的に7だったが、リポジトリの成長に合わせてスケールするようになった。おおよそのオブジェクト数と、誕生日のパラドックスまわりの計算を少し使う」。さっき使ったあの式です。7文字は28ビットで50%に達するのが約1.9万個、Linuxカーネル向け12桁は48ビットで約1,975万個。なるほど。
内部フル桁、曖昧は停止、桁数自動調整。この3つが揃って初めて、短縮表示は成立します。
切る対象がv7なら、生成間隔しだいでTHE ENDです。v7の先頭48ビットはUnixエポックからのミリ秒なので、切り取った8文字の正体はタイムスタンプの上位32ビット。下位16ビットが一周する $2^{16}$ ミリ秒のあいだ、その上位は1ビットも変わりません。
017F22E2 0000 = 2022-02-22 19:21:50.848 UTC ← 窓の始まり
017F22E2 79B0 = 2022-02-22 19:22:22.000 UTC ← RFC 9562 A.6 のテストベクタ
017F22E2 FFFF = 2022-02-22 19:22:56.383 UTC ← 窓の終わり
上の窓に入った1分ちょい(65.536秒)ぶんのv7は、先頭8文字がすべて同じになります。確率がどうという話ですらなく、全件衝突が確定しています。約65秒間、全員が同じ名字の名札を付けて出社してくる状態です、なにそれこわい。
v4の先頭を切ってもランダムは32ビットありましたが、v7の先頭を切るとランダムは0ビットです。確率 100% は 0 NJT。0年連続当選とは?捏造単位すらぶっ壊れました。
結論はひとつです。UUIDを切り落として短くする、という選択肢はありません。 どうしても短くしたいなら、切り落とすのではなく詰め替えること。UUIDが長いのは16進数が1文字あたり4ビットしか運べないからで、文字の種類を増やせばBase64の kZEI91LRQyCbrPhH20FIqA のように、22文字で全128ビットが入ります。それでも桁数が足りないなら、何ビットのランダム性が要るかを先に決めて、そのために設計されたIDを使いましょう。ULID(80ビット)、KSUID(128ビット)、長さを指定できるNanoID(既定126ビット)あたりが入口です。Snowflake IDも短いですが、あれは乱数0ビットで、ノードIDの管理と引き換えに64ビットへ収めた別系統なのでご注意を。
もう1つ、ビットが減る道があります。乱数がしっかり乱数じゃなかった時です。
こちらは切り落としと違って、見た目に一切現れません。
【コラム】乱数を弱くしても、ビットは減る
文字は32文字のまま、見た目も申し分ないUUID。それでも中身の122ビットは埋まっていない、ということが起きます。RFC 9562がCSPRNG(暗号論的に安全な擬似乱数生成器)を使うべきだ(SHOULD)とし、プロセスのフォークのような状態変化のあとは正しく再シードせよと注意しているのは、そのためです。同じシードのまま3つのプロセスを起動すれば、3つとも一字一句同じUUIDを吐きます。切り落としが10万個で2組だったのに対して、こちらは全件が完全一致。しかもバージョンのビットは正しく立っていて、uuid.uuid4() の出力と見分けがつきません。
DebianのOpenSSLパッケージでは、2006年に警告を消す目的で乱数のエントロピー源に手が入り、2008年に公表されるまで気付かれませんでした。CVE-2008-0166 は、影響を受けたバージョン(0.9.8c-1 から 0.9.8g-9 の手前まで)が「予測可能な乱数を生成し、鍵への総当たり攻撃を容易にする」と記録しています。怖いのは、それでも鍵は正しい形式で生成され、正しく動いていたことです。UUIDも同じで、乱数が弱くても正しい見た目の値が出てきますし、テストも通ります。確かめるなら、出力ではなく実装を読むしかありません。
逆方向の注意も1つ。CSPRNGを正しく使っていても、UUIDを推測困難な秘密として扱ってはいけません。RFC 9562 は、UUIDが推測しにくいと仮定するな(SHOULD NOT)、持っているだけでアクセスが通る識別子に使うな(MUST NOT)と名指ししています。招待リンクやリセットトークンが要るなら、そのために設計されたものを別に用意してください。
切り落としも弱い乱数も、あの確率表の前提を崩します。分母が $2^{122}$ でなくなった瞬間、宝くじで掴んだあの桁数の感覚はまるごと根拠を失ってしまいます。
10万個で3.70NJTだったはずが、切り落とせば0.02NJT、乱数を壊せば 0 NJT。しかも厄介なことに、何ビットくらいまで落ちたのかは出力を眺めてもまったく分かりません。
「UUIDが重複しました」報告の顛末
衝突報告を聞いたときは、3.98NJTぞ!?と思いましたが、詳しく聞くと実装の問題だったようで。
1件目、拍子抜けしました。00000000-0000-0000-0000-000000000000。Nil UUID です。RFC 9562 が「ここには値が無い」を表すために予約している特別な値で、ついでに全ビットが1の Max UUID(ffffffff-…)も終端マーカ用に用意されています。生成に失敗したライブラリが黙ってこれを返し、初期値のまま2行入っていました。
2件目が厄介でした。deadbeef-… と DEADBEEF-… が、別レコードとして並んでいる。UUIDとしては同じ値です。RFC 9562 のABNFは HEXDIG に大文字を採ったうえで、英字は全部大文字でも全部小文字でも混在でもよいと明記しています。RFC 4122 が「出力は小文字」としていたところを、9562はそこすら緩めました。仕様が許しているのだから、大小が混ざってくるのは当たり前です。
ところがカラムは varchar(36)。文字列としては別物なので、ユニーク制約は素通り、JOINは空を返し、集計が二重になって発覚しました。RFC 9562 は「DBにはUUIDを128ビットのバイナリで格納すべき(SHOULD)」とし、文字列にすると288ビット要ると計算までしてくれています。uuid 型があるなら使いましょう。大文字小文字の正規化はDBのお仕事です。
3件目は、冪等性ロジックの実装ミスでした。リトライで同じUUIDを再送され、ユニーク制約が弾いていました。二重登録を防ぐ正常な衝突を、異常として上げていました。
修正後、衝突報告はパッタリと無くなりました。結局、宝くじは買わずに済みました。
設計は「何NJTか」で決めない
さんざん桁を数えておいて言うのもなんですが、確率で設計を決めてはいけません。
RFC 9562 の「Collision Resistance」節にあるのは、衝突したときの影響を見積もれという指示です。
挙げられている例が両極端です。
影響が小さい例として「ログのエントリが重複して、集計値が少しずれる」。
影響が大きい例として「キー重複で、航空機が誤った針路を受け取り、人命が危険に晒される」。
後者では失敗が許されないので、そのアプリケーションの文脈で可能な限りの衝突耐性を用意しろ、と続きます。集計値が少しずれるだけなら0.27NJT(1%)でも無視して構いませんし、消えたら二度と追えないデータなら3.98NJTでも制約を張る。同じ数字が、用途によって「無視していい」にも「対策しろ」にもなる。
UUIDの衝突は滅多に起きないから考えなくていい、では無く、衝突すると何が壊れるかを設計に落とし込むべきだ、ということです。
決めるべき大方針はたった1つ、衝突したイベントを握り潰さないこと。
主キーやユニーク制約を張っておけば、重複はエラーとして表に出ます。ただ、張っただけでは足りません。ON CONFLICT DO UPDATE や PutItem みたいな「あれば上書き」で書いていると、衝突は「エラー」ではなく「いつの間にか消えたデータ」になります。気付いたときには原因を追えません(そもそも気付けるのか?)。
衝突イベントとして出てくれさえすれば、拾ってフォロー出来ます。発生後にどうするのかは、確率の大小とは別の判断に出来ます。何NJTだろうと、ゼロにはならないのですから。
まとめ
冒頭の3.98NJTも、表に並べた数字も、全122ビットが乱数である前提の上に成り立っています。
バージョンの選び方次第で 0 ビット(論外)にも、70ビット台(実用上は十分)にもなるし、8文字に切れば32ビット(ヤバい)になる。1億個の2.88NJTが 0 NJTまで落ちます。捏造単位を支配していたのは、ビット数だったのです。(そのビットの内訳まで見れたらなお良し)
あのときの設計レビューに戻れるなら、今度は数字で答えられます。
「1万件なら3.98NJTですわ(ドヤァ)」
「は?」と返ってくるでしょうから、続けます。「年末ジャンボの1等を4年連続で当てるのと同じ桁です。それより、衝突したときに黙って上書きされる作りになってません?」
今度は、議題が次へ進んでくれないかもしれません。けど、それでいいのだと思います。
お断り
記事内容は個人の見解であり、所属組織の立場や戦略・意見を代表するものではありません。
あくまでエンジニアとしての経験や考えを発信していますので、ご了承ください。
文中の「ジャンボ宝くじ」「年末ジャンボ」をはじめ、記載の会社名・製品名・サービス名は各権利者の商標または登録商標です。本文中では ™ および ® を省略しています。また、当せん確率は公表されている当せん本数から筆者が計算したもので、主催元の発表値ではありません。
-
ジャンボ宝くじは10万枚を1組として、年末ジャンボなら200組(2,000万枚)で1ユニットを構成し、1ユニットにつき1等が1本です。ここから計算した値になります。1等の組違い賞は、1等と同じ番号で組が違うもの、つまり1ユニットに199本あるので $199 / 20{,}000{,}000$、およそ10万分の1になります。末等の10%は、番号の下1桁が0から9まで均等に振られていることから導けます。当せん本数の内訳は宝くじ公式サイトの発表(2025年の年末ジャンボ)にあります。ユニットの構成も当せん本数も回によって変わり、この発表ページ自体も入れ替わるため、最新の値は主催元の発表をご確認ください。 ↩
-
$n \ll N$ のときによく使われる近似です。$p$ が十分小さい範囲では、さらに簡単に $p \approx \dfrac{n^2}{2N}$ と考えて構いません。NJTの表もこの簡易式で計算しています。例外が4行あります。確率がパーセント単位に入ると簡易式は上振れするので271京個と106京個の2行は上の指数の式を、$n$ が1桁になる2個と2.4個の2行は $n^2$ と $n(n-1)$ が2倍近く違ってしまうのでペアの数そのもの $\dfrac{n(n-1)}{2N}$ を使っています。 ↩
-
2024年5月に発行され、長らく参照されてきた RFC 4122 を廃止(Obsoletes)しました。v6・v7・v8が加わったのもこの版からです。手元の資料が4122を指していたら、それは2005年の版になります。 ↩