文字列に適したハッシュ関数


160

文字列に適したハッシュ関数を考えています。そして、文字列の最初の5文字のUnicode値を合計するのは良い考えかもしれないと考えていました(5文字であると想定し、それ以外の場合は終了する場所で停止します)。それは良い考えでしょうか、それとも悪い考えですか?

私はこれをJavaで行っていますが、それが大きな違いをもたらすとは思いません。


4
優れたハッシュ関数は、ハッシュへの入力とアルゴリズムの要件に大きく依存します。たとえば、すべての文字列が同じ5文字で始まる場合、そのようなハッシュはあまり良くありません。また、正規分布になる傾向があります。
WhirlWind 2010


14
なぜString自分のものを使用できないのですhashCode()か?
Bart Kiers、2010

@WhirlWind、本当、私は文字列が何になるかわかりませんが、それ以外はおそらく英語のテキストになります。
Leif Andersen

@Barl、主に私の教授が私たち自身のハッシュ関数を実装するように言ったから...そして私がJavaを使いたくなかった理由は、それが一般的であったからであり、より具体的なハッシュ関数がより良いだろうと私は想像しました。
Leif Andersen

回答:


161

通常、ハッシュはそうでない場合、合計しないだろうstoppots同じハッシュを持つことになります。

また、最初のn文字に制限しないと、家と家のハッシュが同じになるためです。

通常、ハッシュは値を取り、素数を掛けます(一意のハッシュを生成する可能性が高くなります)。したがって、次のようなことを行うことができます。

int hash = 7;
for (int i = 0; i < strlen; i++) {
    hash = hash*31 + charAt(i);
}

@jonathanasdfそれが常にあなたにユニークなハッシュキーを与えると言うことができますか 数学的な証明はありますか?別の大きな素数でハッシュのmodを取る必要があると思います。そうしないと、オーバーフローの問題が発生します。
devsda 2013

17
@devsda彼はいつもユニークであるとは言わず、ユニークである可能性が高いと言った。なぜに関しては、Googleでクイック検索には、この記事を明らかに:computinglife.wordpress.com/2008/11/20/... 31は、Java文字列のハッシュを使用した理由を説明します。数学的な証明はありませんが、素数がよりよく機能する理由に関する一般的な概念を説明しています。
Pharap 2013

2
より良いハッシュを行うという考えを明確にしてくれてありがとう。念のため再確認します-hashCode()の戻り値は、オブジェクトを格納する前に、Javaがテーブルインデックスにマップするために使用されます。したがって、hashCode()がmを返す場合、(m mod k)のような処理を行って、サイズkのテーブルのインデックスを取得します。そうですか?
ホワイトハット2015年

1
「ハッシュ= hash * 31 + charAt(i);」スポット、トップ、ストップ、オプト、ポットに対して同じハッシュを生成します。
ジャックストラウブ2018年

1
@maq私はあなたが正しいと信じています。私が何を考えていたかわからない。
ジャックストラウブ2018

139

セキュリティが必要な場合は、Java暗号を使用できます。

import java.security.MessageDigest;

MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(stringToEncrypt.getBytes());
String encryptedString = new String(messageDigest.digest());

93
いいね。大規模なコーパスに対して統計NLPを実行する機械学習アプリケーションがあります。テキスト内の元の単語の形態学的正規化の最初のいくつかのパスの後、文字列値を破棄し、代わりにハッシュコードを使用します。コーパス全体で、約600,000の一意の単語があり、デフォルトのJavaハッシュコード関数を使用して、約3.5%の衝突を取得していました。しかし、文字列値をSHA-256してから、ダイジェストされた文字列からハッシュコードを生成すると、衝突率は0.0001%未満になります。ありがとう!
benjismith

3
衝突と単語数についての情報を提供してくれてありがとう。非常に役立ちます。
フィリップ

19
@benjismith 100万分の1は大きすぎます...「0.0001%未満」は「正確に0」と言う斜めの方法ですか?SHA-256の衝突を目にしたことは本当にありません。160ビットSHA-1の場合も同様です。同じSHA-256を生成する2つの文字列がある場合、セキュリティコミュニティはそれらを確認することを望みます。あなたは世界的に有名になります...非常にあいまいな方法で。参照SHA機能の比較
ティム・シルベスター

7
@TimSylvester、あなたは誤解しました。SHA-256の衝突は見つかりませんでした。32ビットのハッシュが必要だったので、SHA-256を計算し、結果のバイトシーケンスを典型的なJavaの「hashCode」関数に送りました。そこで衝突を見つけました。特筆すべきものはありません:)
benjismith

1
「ハッシュ」と「暗号化」の違いはありませんか?MessageDigestが一方向ハッシュ関数であることを理解していますよね?また、この関数を使用すると、LibreOfficeでファイルを開いたときに、ハッシュされた文字列が大量のジャンクUTF文字として取得されました。ハッシュされた文字列を、迷惑なUTF文字ではなくランダムな英数字の束として取得することは可能ですか?
Nav 2016年

38

おそらくString.hashCode()を使用する必要があります。

あなたが実際にhashCodeを実装したい場合:

パフォーマンスを向上させるために、オブジェクトの重要な部分をハッシュコードの計算から除外したくありません-Joshua Bloch、Effective Java

最初の5つの文字を使用することである悪い考え。URLなどの階層名について考えてみてください。それらはすべて同じハッシュコードを持ちます(すべて「http://」で始まるため、ハッシュマップの同じバケットの下に格納され、ひどいパフォーマンスを示します。

これは、「Effective Java」のString hashCodeを言い換えた戦争の話です。

1.2より前のすべてのリリースで実装された文字列ハッシュ関数は、最初の文字から始めて、文字列全体に等間隔に配置された最大16文字を調べました。URLなどの階層名の大規模なコレクションの場合、このハッシュ関数はひどい動作を示しました。


1
ダブルハッシュコレクションを使用している場合、最初のハッシュを非常に高速でダーティにすることは価値があります。1000の長い文字列があり、そのうちの半分がクラム関数によって特定の値にマップされ、その半分が個別の値にマップされている場合、シングルハッシュテーブルのパフォーマンスは悪くなりますが、ダブルハッシュのパフォーマンスは2番目のハッシュが文字列全体を検査するハッシュテーブルは、単一ハッシュテーブルのほぼ2倍になる可能性があります(文字列の半分を完全にハッシュする必要がないため)。ただし、標準のJavaコレクションはダブルハッシュを行いません。
スーパーキャット2014年

有効なJavaリンクが壊れています@Frederik
KGs

17

あなたがJavaでこれをしているなら、なぜそれをしているのですか?.hashCode()文字列を呼び出すだけ


2
私はクラスの一部としてそれをやっていて、割り当ての一部はいくつかの異なるハッシュ関数を書くことです。教授は私たちに「より良い」もののために外部の助けを得るように言った。
Leif Andersen

20
JVMのバージョンと実装全体で一貫性を保つ必要がある場合は、に依存しないでください.hashCode()。むしろ、いくつかの既知のアルゴリズムを使用してください。
Stephen Ostermiller 2013年

7
のアルゴリズムString::hashCodeはJDKで指定されているため、クラスの存在そのものと同じくらい移植可能java.lang.Stringです。
yshavit 2015


8

Nickが提供するこの関数は優れていますが、new String(byte [] bytes)を使用してStringに変換すると失敗します。この機能を使用してそれを行うことができます。

private static final char[] hex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

public static String byteArray2Hex(byte[] bytes) {
    StringBuffer sb = new StringBuffer(bytes.length * 2);
    for(final byte b : bytes) {
        sb.append(hex[(b & 0xF0) >> 4]);
        sb.append(hex[b & 0x0F]);
    }
    return sb.toString();
}

public static String getStringFromSHA256(String stringToEncrypt) throws NoSuchAlgorithmException {
    MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
    messageDigest.update(stringToEncrypt.getBytes());
    return byteArray2Hex(messageDigest.digest());
}

これは誰かを助けることができるかもしれません


バイト配列をmessageDigest.update()に渡すだけです。
szgal

byteArray2Hex()-それは私が探していたものとまったく同じです!
どうも


5

FNV-1は文字列に適したハッシュ関数であると噂されています。

長い文字列(たとえば、約200文字より長い)の場合、MD4ハッシュ関数から優れたパフォーマンスを得ることができます。暗号化機能として、約15年前に破られましたが、非暗号化の目的では、それはまだ非常に優れており、驚くほど高速です。Javaのコンテキストでは、16ビットのchar値を32ビットのワードに変換する必要があります。たとえば、そのような値をペアにグループ化するなどです。JavaでのMD4の高速実装は、sphlibにあります。おそらく、教室の割り当てのコンテキストではやりすぎですが、それ以外は試す価値があります。


このハッシュ関数は、javaに付属するものよりもはるかに優れています。
clankill3r

3

業界標準の実装を見たい場合は、java.security.MessageDigestを参照してください。

「メッセージダイジェストは、任意サイズのデータ​​を受け取り、固定長のハッシュ値を出力する安全な一方向ハッシュ関数です。」


1

ここに多くの異なるハッシュ関数を説明するリンクがあります。今のところ、私はあなたの特定の問題のためにELFハッシュ関数を好みます。入力として任意の長さの文字列を受け取ります。


1

sdbm:このアルゴリズムは、sdbm(ndbmのパブリックドメインの再実装)データベースライブラリ用に作成されました

static unsigned long sdbm(unsigned char *str)
{   
    unsigned long hash = 0;
    int c;
    while (c = *str++)
            hash = c + (hash << 6) + (hash << 16) - hash;

    return hash;
}

0
         public String hashString(String s) throws NoSuchAlgorithmException {
    byte[] hash = null;
    try {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        hash = md.digest(s.getBytes());

    } catch (NoSuchAlgorithmException e) { e.printStackTrace(); }
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < hash.length; ++i) {
        String hex = Integer.toHexString(hash[i]);
        if (hex.length() == 1) {
            sb.append(0);
            sb.append(hex.charAt(hex.length() - 1));
        } else {
            sb.append(hex.substring(hex.length() - 2));
        }
    }
    return sb.toString();
}

-1

文字列の良い速関数を開発しようとするときに奇数で作業するのは良い考えです。この関数は文字列を取り、インデックス値を返します。これまでのところ、その動作はかなり良好です。衝突が少ない。インデックスの範囲は0〜300ですが、それ以上かもしれませんが、「電気機械工学」のような長い単語を使っても、これまでこれ以上高くはありません。

int keyHash(string key)
{
    unsigned int k = (int)key.length();
    unsigned int u = 0,n = 0;

    for (Uint i=0; i<k; i++)
    {
        n = (int)key[i];
        u += 7*n%31;
    }
    return u%139;
}

あなたができる別のことは、単語「クマ」(0 * b)+(1 * e)+(2 * a)+(3 * r)のように増加するにつれて、各文字のintパースをインデックスで乗算することです遊ぶint値。上記の最初のハッシュ関数は、「ここ」と「聞く」で衝突しますが、いくつかの優れた一意の値を提供するのに優れています。以下のコードは、「ここ」や「聞く」と衝突しません。増加するにつれて、各文字にインデックスを掛けているからです。

int keyHash(string key)
{
    unsigned int k = (int)key.length();
    unsigned int u = 0,n = 0;

    for (Uint i=0; i<k; i++)
    {
        n = (int)key[i];
        u += i*n%31;
    }
    return u%139;
}

-1

これが、私が作成したハッシュテーブルに使用する簡単なハッシュ関数です。基本的にはテキストファイルを取得し、アルファベット順を表すインデックスにすべての単語を格納します。

int generatehashkey(const char *name)
{
        int x = tolower(name[0])- 97;
        if (x < 0 || x > 25)
           x = 26;
        return x;
}

これが基本的に行うことは、単語が最初の文字に従ってハッシュされることです。したがって、「a」で始まる単語のハッシュキーは0になり、「b」は1になり、「z」は25になります。数値と記号のハッシュキーは26になります。 ; 特定の単語がハッシュテーブルのどこにインデックス付けされるかを簡単かつ迅速に計算できます。これは、次のようにすべてがアルファベット順であるためです。コードは、次の場所にあります:https : //github.com/abhijitcpatil/general

次のテキストを入力として与える: Atticusはある日Jemに次のように言いました。「裏庭のブリキ缶で撃ったほうがいいですが、鳥を追いかけるつもりです。あなたがそれらを打つことができるなら、あなたが望むすべてのブルージェイズを撃ってください、しかしそれがモッキンバードを殺すのは罪であることを忘れないでください。」アッティカスが何かをするのは罪だと言うのを聞いたのはそのときだけだったので、ミス・オーディに聞いた。「あなたの父親の権利」と彼女は言った。「モッキンバードは私たちが楽しむために音楽を作る以外に何もしません。彼らは人々の庭を食べ尽くしたり、トウモロコシのベビーベッドに巣を作ったりすることはありません。彼らは一つのことをするのではなく、私たちのために彼らの心を歌います。だからこそ、モッキンバードを殺すのは罪なのです。

これは出力になります:

0 --> a a about asked and a Atticus a a all after at Atticus
1 --> but but blue birds. but backyard
2 --> cribs corn can cans
3 --> do dont dont dont do dont do day
4 --> eat enjoy. except ever
5 --> for for fathers
6 --> gardens go
7 --> hearts heard hit
8 --> its in it. I it I its if I in
9 --> jays Jem
10 --> kill kill know
11 --> 
12 --> mockingbird. music make Maudie Miss mockingbird.”
13 --> nest
14 --> out one one only one
15 --> peoples
16 --> 17 --> right remember rather
18 --> sin sing said. she something sin say sin Shoot shot said
19 --> to Thats their thing they They to thing to time the That to the the tin to
20 --> us. up us
21 --> 
22 --> why was was want
23 --> 
24 --> you you youll you
25 --> 
26 --> Mockingbirds  Your em Id

2
優れたハッシュ関数は、値をバケット全体に均等に分散します。
ジョナサンピーターソン

-1

これにより、衝突が回避され、計算でシフトを使用するまで高速になります。

 int k = key.length();
    int sum = 0;
    for(int i = 0 ; i < k-1 ; i++){
        sum += key.charAt(i)<<(5*i);
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.