「PEP 456: Secure and Interchangeable Hash Algorithm」なハナシ

見出しに反して、てーしたハナシをしてえわけぢゃぁない。

Python 3.4 ではデフォルトで使われるオブジェクトのハッシュ化アルゴリズムが、従来までの FNV から SipHash24 に置き換えられた、というのがこれ (PEP 456)。

ハッシュアルゴリズムコレクター、てことでもないんだけれども、ハッシュ化のアルゴリズムの選択肢が増えるといつもなんか嬉しくなる癖があって。なんでかといえば、「アプリケーションのライフサイクル内で実用的に衝突しなければ十分」というニーズでの「ビット数が大き過ぎなくてそれなりに高速なハッシュ」は便利だから。例えばファイルのキャッシュに使うキーとかだよね。

てなわけでふと「SipHash24 が標準ライブラリとして公開されてたりしないんかな?」と期待した。けどこれは少し違ってる。「選べないけど使える」ということ。んー、まぁそりゃそうか…。:

 1 Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:43:06) [MSC v.1600 32 bit (Intel)] on win32
 2 Type "help", "copyright", "credits" or "license" for more information.
 3 >>> import sys
 4 >>> sys.hash_info
 5 sys.hash_info(width=32, modulus=2147483647, inf=314159, nan=0, imag=1000003, algorithm='siphash24', hash_bits=64, seed_bits=128, cutoff=0)
 6 >>> # Python 3.3 からはインタプリタ実行ごとに生成するハッシュ値が違う (ハッシュのランダム化)
 7 >>> # ので、永続化目的には使えない。
 8 >>> hash(b"abcdefg")
 9 524660144
10 >>> hex(hash(b"abcdefg"))
11 '0x1f45adb0'
12 >>> 

PEP によれば object、bytesobject、memoryobject、unicodeobject、datetimemodule のデフォルトハッシュアルゴリズムを siphash24 に置き換えたようなので、実行例のとおりバイト列に対する hash で siphash24 が呼ばれて飛び出てる、はず。

実行例のコメントに書いた通り、選べないけど使える「けれども用途が限定される」、のな。ワタシの動機では使えんのよ。そいつぁ弱ったの、っと。ほんとは hashlib の一味にしれーっと入れといて欲しいんだがねぇ。(今確認してみたが hashlib.algorithms_available に入ってない。まぁオブジェクトのハッシュ値用途と hashlib のテリトリーは確かに違うわけだしね、入ってたら入ってたで「誤解して使ってしまう」ユーザが増殖して困ったりもするんであろう。)

なお、該当する実装は pyhash.c。もちろんこの元のコードがあれば移植するなりのことは出来るけれど、ハッシュ関数と暗号化は実際「バイナリの扱いが非常に繊細」なので実際はとても骨が折れる。(エンディアン、32ビット機か64ビット機か、signed か unsigned か、もそうだけれど、「桁溢れもアルゴリズムの一部」だしね、とにかく大変なのね。)

なんてちょっとばかり不毛なことを考えていた。まぁこんなんは似たことを考える人は必ずいてさ、PyPI にあるかもしれん。あとで探してみよう。










ところで関連してハッシュの話で、やや古めの記事だけれどこんなのを見つけた:

かなり読まれてる記事みたいね。なんかアタシもこの記事が言ってる違和感と似た話をしたこともあるんで、結構なでじゃぶー。思うに。知識のあるなしってことじゃあなくてさ、「目の前の問題を正確に理解し、その解法のテリトリー、レンジと正しくマッピングする」ということに、どれだけ誠実に向き合うかどうか、ってことなんだと思うのね。引用したサイトで色々例を挙げてる通りね。きちんと順序立てて考えてれば、こんなことが起こることは少なくて済むはず、なのです。