web-dev-qa-db-ja.com

python)での高速、大幅、非暗号化文字列ハッシュ

pythonで、少なくとも4ビットの出力を持つ整数を生成する高性能の文字列ハッシュ関数が必要です(64ビットは理にかなっていますが、32は少なすぎます)。 Stack Overflowには、このような他のいくつかの質問がありますが、私が見つけたすべての受け入れられた/賛成された回答の中で、(与えられた理由で)当てはまらないいくつかのカテゴリの1つに分類されました。

  • 組み込みのhash()関数を使用します。この関数は、少なくとも私が開発しているマシンでは(python 2.7、および64ビットCPUを使用) 32ビット以内に収まる整数を生成します-私の目的には十分な大きさではありません。
  • hashlibを使用します。 hashlibは暗号化ハッシュルーチンを提供します。これは、暗号化以外の目的で必要とされるよりもfar低速です。これは自明のことですが、この事実を納得させるためにベンチマークと引用が必要な場合は、それを提供できます。
  • string.__hash__()関数をプロトタイプとして使用して、独自の関数を作成します。この特定の関数の効率がc_mulの使用にあることを除いて、これが正しい方法だと思います。 32ビットをラップする関数-繰り返しますが、私が使用するには小さすぎます!非常にイライラします、それは完璧にとても近いです!

理想的なソリューションは、次の特性を、比較的重要度の低い順に持っています。

  1. consistent avalanche property over all bitsを維持しながら、出力範囲を少なくとも34ビット(おそらく64ビット)に拡張します。 (32ビットハッシュを連結すると、少なくとも私のばかげた例では、雪崩特性に違反する傾向があります。)
  2. ポータブル。 2つの異なるマシンで同じ入力文字列が与えられた場合、どちらの場合も同じ結果が得られるはずです。これらの値は、後で再利用できるようにファイルに保存されます。
  3. ハイパフォーマンス。この関数は、実行中のプログラムの実行中に約200億回呼び出されるため、高速であるほど優れています(現時点ではパフォーマンスが重要なコードです)。Cで記述する必要はありません。実際にはmd5(文字列の組み込みhash()の領域のどこか)よりもパフォーマンスが優れている必要があります。
  4. 出力を変更するための入力として「摂動」(ここで使用するのに適したWordは何ですか?)整数を受け入れます。以下に例を示します(リストのフォーマット規則では、近くに配置することはできません)。関数の出力を手動で摂動させることでシミュレートできるため、これは100%必要ではないと思いますが、入力として使用すると、素敵な暖かい感じ。
  5. 完全にPythonで書かれています。絶対に、積極的にneeds Cで書くなら、それはできると思いますが、pythonで書かれた関数はCの速い関数よりも20%遅い関数を取ります。 2つの異なる言語を使用することによるプロジェクト調整の頭痛の種が原因です。はい、これは警官ですが、これはここのウィッシュリストです。

「摂動」ハッシュの例。ハッシュ値は小さな整数値nによって大幅に変更されます。

def perturb_hash(key,n):
    return hash((key,n))

最後に、私が何をしているのか知りたい場合は、そのような特定のハッシュ関数が必要です。パフォーマンスを大幅に向上させるために、pybloomモジュールを完全に書き直しています。私はそれで成功しました(今では約4倍速く実行され、スペースの約50%を使用します)が、フィルターが十分に大きくなると、突然偽陽性率が急上昇することに気づきました。ハッシュ関数が十分なビットをアドレス指定していなかったためだと気づきました。 32ビットは40億ビットしかアドレス指定できず(フィルターはバイトではなくビットをアドレス指定します)、ゲノムデータに使用しているフィルターの一部はその2倍以上(したがって最小34ビット)です。

ありがとう!

37
eblume

MurmurHash3の128ビットバリアント を見てください。 アルゴリズムのページ にはいくつかのパフォーマンス数値が含まれています。これをPythonに、純粋に、またはC拡張として移植できるはずです。 (更新作成者は、128ビットバリアントを使用し、不要なビットを破棄することをお勧めします)。

MurmurHash2 64ビットが機能する場合は、 pyfasthashパッケージ にPython実装(C拡張)があります。これには、他のいくつかの非暗号化ハッシュバリアントが含まれています。これらのいくつかは32ビット出力しか提供しませんが。

UpdateMurmur3ハッシュ関数のクイックPythonラッパーを実行しました。 Githubプロジェクトはここにあります =そしてあなたはそれを Python Package Indexも で見つけることができます;それはビルドするためにC++コンパイラを必要とするだけです;ブーストは必要ありません。

使用例とタイミング比較:

import murmur3
import timeit

# without seed
print murmur3.murmur3_x86_64('samplebias')
# with seed value
print murmur3.murmur3_x86_64('samplebias', 123)

# timing comparison with str __hash__
t = timeit.Timer("murmur3.murmur3_x86_64('hello')", "import murmur3")
print 'murmur3:', t.timeit()

t = timeit.Timer("str.__hash__('hello')")
print 'str.__hash__:', t.timeit()

出力:

15662901497824584782
7997834649920664675
murmur3: 0.264422178268
str.__hash__: 0.219163894653
23
samplebias

組み込みのhash()関数を使用します。この関数は、少なくとも私が開発しているマシン(python 2.7、および64ビットCPU)では、32ビット内に収まる整数を生成します-私の目的には十分な大きさではありません。

それは真実ではない。組み込みのハッシュ関数は、64ビットシステムで64ビットハッシュを生成します。

これはpython str hashing function from Objects/stringobject.c(Pythonバージョン2.7):

static long
string_hash(PyStringObject *a)
{
    register Py_ssize_t len;
    register unsigned char *p;
    register long x;      /* Notice the 64-bit hash, at least on a 64-bit system */

    if (a->ob_shash != -1)
    return a->ob_shash;
    len = Py_SIZE(a);
    p = (unsigned char *) a->ob_sval;
    x = *p << 7;
    while (--len >= 0)
        x = (1000003*x) ^ *p++;
    x ^= Py_SIZE(a);
    if (x == -1)
        x = -2;
    a->ob_shash = x;
    return x;
}
3
Saish

「文字列」:ハッシュPython 2.x strオブジェクトおよび/またはPython3.xbytesおよび/またはbytearrayオブジェクト。

これは最初の制約に違反する可能性がありますが、次のようなものの使用を検討してください。

(zlib.adler32(strg, perturber) << N) ^ hash(strg)

(32 + N)ビットハッシュを取得します。

2
John Machin

組み込みのハッシュ関数に注意してください!

Python3以降、インタープリターが起動するたびに異なるシードが供給されるため(詳細はわかりません)、毎回異なる値が生成されますが、ネイティブの数値型では生成されません。

$ python3 -c 'print(hash("Hello!"), hash(3.14))'
-1756730906053498061 322818021289917443
$ python3 -c 'print(hash("Hello!"), hash(3.14))'
-4556027264747844925 322818021289917443
$ python3 -c 'print(hash("Hello!"), hash(3.14))'
-4403217265550417031 322818021289917443
0
packmad

Python 3.2を使用できる場合、64ビットWindowsでのハッシュ結果は64ビット値になります。

0
casevh