
2026/05/29 1:11
すべて64ビット整数のうち、2つの32ビット整数の積であるものがわずか17%しか存在しない
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
核心となるメッセージは、固定幅整数の乗算を数学的に逆算しようとするのは誤りであるということであり、それゆえに得られる積は均匀に分布していないことに起因している。具体的には、あらゆる可能な 64 ビットの値の中で、2 つの 32 ビット整数の積として表現できるものは約 17%(より小さな桁幅ではおよそ 20%)しかなく、ビット幅が増加するにつれてこの比率はゼロに近づく。8 ビット算術における具体的なオーバーフローの例として、127 × 127 = 16129 が 1 に切り捨てられることが示される。2 つの 32 ビット値を乗算するには 64 ビットの精度が必要であるが、素因数分解を用いない乗算の逆算を試みるアルゴリズムは、一般的なケースで失敗する。これは、ほぼすべての 64 ビット整数が 2 つの 32 ビット整数の積ではないためである。これは、一様分布を仮定しているにもかかわらず、予測可能な衝突を生み出すクリプティックな設計やハッシュ関数(例:“clhash"または単純な高/低分割)に対して決定的な影響を持つ。開発者は、直感的な乗算ベーススキームの脆弱性を認識し、操作を逆算する際には適切な因数分解手法を用いるべきである。
Text to translate
The core message is that reversing fixed-width integer multiplication is mathematically flawed because products are far from uniformly distributed: only about 17% of all possible 64-bit values can be expressed as the product of two 32-bit integers (roughly 20% for smaller widths), and this proportion approaches zero as bit width increases. A concrete overflow example shows that in 8-bit arithmetic, 127 × 127 = 16129 is truncated to 1. While multiplying two 32-bit values requires 64 bits of precision, algorithms that attempt to reverse multiplication without prime factorization will generally fail, since most 64-bit integers are not products of two 32-bit integers. This has critical implications for cryptographic designs and hash functions (e.g., “clhash” or simple high/low splits) that assume uniform distribution but instead produce predictable collisions. Developers should recognize the weakness of naive multiplication-based schemes and rely on proper factorization methods when reversing operations.
本文
2 つの 32 ビット整数の積として表現可能な 64 ビット値の割合について
完全な積(Full Product)の概念
- ソフトウェアプログラミングにおいて、固定精度(例:8 ビット)で乗算を行うとオーバーフローが発生することがある。
- 127 × 127 を 8 ビット無符号整数で計算すると結果は 1 になるが、正確な積は 16,129。
- この正確な積を「完全な積(full product)」と呼ぶ。
- 一般的に、2 つの 32 ビット整数を掛け合わせると、その積は 64 ビット で表現される。
なぜこの比率が重要なのか?
- 「すべての 64 ビット整数の中で、2 つの 32 ビット整数の積として表せるものの割合」がなぜ重要かについては、ハッシュ関数の設計における応用がある。
- ハッシュ関数は入力からランダム性を確保した出力を生成する必要があるため、乗算特性を知ることは設計上重要である。
- 著者は文字列に対して高速なハッシュ関数
を開発しており、同様の数学的知見が役立つことを示唆している。clhash
暗号分野における乗算の採用
- ハッシュ関数設計には、標準的な単純乗算に基づく技術との比較・優位性を示す目的で、特定の乗算手法が用いられる。
- 32 ビット整数に対する簡易なハッシュ関数の例:
- 下位 16 ビットと上位 16 ビットの積を計算する設計。
// simpleHighLowHash は 32 ビットの簡易的でありながら弱いハッシュ関数です。 // これは上位 16 ビットと下位 16 ビットの積を計算します。 func simpleHighLowHash(x uint32) uint32 { high := uint16(x >> 16) low := uint16(x & 0xFFFF) return uint32(high) * uint16(low) }
ハッシュ出力の一様性に関する理論的背景
- 理想的にはハッシュ出力は一様(すべての値が等確率で出現)でありたい。
- しかし、単純な積計算では全ての 32 ビット値を生成することはできない。
- エルディシュの定理:
- $n$ ビットの積として生成可能な値の割合は、$n$ が大きくなるにつれて 0 に収束する。
- 例:$10^7$ ビットの整数同士を掛けると、期待される出力空間の中では実現可能なものが非常に少ない。
現実的なケース(32 ビット × 32 ビット → 64 ビット)での比率的分析
小さなデータサイズにおける結果
- 16 ビット × 16 ビット → 32 ビット の場合:
- 全探索(Brute-force)で容易に計算可能。
- 生成される 32 ビット整数のうち、約 5 つに 1 つ(20%)のみが積として表現可能。
- 残りの 80% はこの手法では決して生成されない。
32 ビット × 32 ビットの場合の困難さ
- 全探索を行うと計算量が指数関数的に増大するため、実質的に不可能。
- 以下の関数は、32 ビット整数同士を掛けて 64 ビット値を得る簡易な例:
func simpleHighLowHash(x uint64) uint64 { high := uint32(x >> 32) low := uint32(x & 0xFFFFFFFF) return uint64(high) * uint64(low) }
正確な割合の算出結果
-
ウエストバー氏(Westerborg)らの研究により、数式的アプローチで正確な値が計算可能。
-
結論: 2 つの 32 ビット整数の積として表現可能な 64 ビット無符号整数の総数は以下の通り。
3,215,709,724,700,470,902
-
これは、すべての可能な 64 ビット値のうち約 17% に相当する。
積から元の因数を復元する問題
復元の手法(素因数分解を利用)
与えられた積 $N$ から、元の 2 つの 32 ビット整数 $(a, b)$ を見つける手順:
- 完全な素因数分解を行う。
- 素数因子を利用して、厳密に $2^{32}$ より小さい全ての可能な除数を構築する。
- 重複のない素数因子とその冪ごとに処理する。
- システム的に候補集合を更新し、$2^{32}$ を超えない積のみを残す。
- 生成された候補の中で最大の値 $m$ を選ぶ。
- それに応じて残りを計算 ($n/m$) し、適切な分割が存在するかを確認する。
注意: 解は一般に一意ではないが、本実装では「どちらかの値が最大化されるような対」を返す。
Python による実装例(簡易ロジック)
# 素数因子 f とその冪 multiplicities を用いて候補を作成 for p in factor_multiplicities: new_candidates = [] for c in candidates: for i in range(factor_multiplicities[p] + 1): if c * (p ** i) < 2**32: new_candidates.append(c * (p ** i)) for new_c in new_candidates: candidates.append(new_c) m = max(candidates) print(f"Maximum candidate: {m}") leftover = n // m print(f"Leftover: {leftover}") if leftover >= 2**32: print("左残りは大きすぎます。適切な候補は見つかりません。")
結論:大多数の値は積として表せない
- ランダムに選ばれた 64 ビット整数を、2 つの 32 ビット整数の積として表現しようとした場合、ほぼ間違いなく「該当しない」という結果になる。
- すべての 64 ビット整数のほとんどが、単純な 2 つの 32 ビット整数の積として表せるわけではないという事実を理解することが重要である。