2010-04-12 84 views
105

我想爲字符串想好散列函數。我認爲總結字符串中前五個字符的unicode值可能是個好主意(假設它有五個字符,否則停止它的結束位置)。這是一個好主意,還是一個壞主意?良好的字符串散列函數

我在Java中這樣做,但我不會想象這會有很大的不同。

+4

好的哈希函數在很大程度上取決於輸入到哈希,和算法的要求。例如,如果您的所有字符串都以相同的五個字符開頭,那麼這樣的散列就不會很好。它也會導致正常分佈。 – WhirlWind 2010-04-12 17:58:54

+0

[98153]的可能重複(http://stackoverflow.com/questions/98153/whats-the-best-hashing-algorithm-to-use-on-a-stl-string-when-using-hash-map) – 2010-04-12 17:59:13

+10

爲什麼你不能使用'String'自己的'hashCode()'? – 2010-04-12 18:01:28

回答

116

通常散列不會做和,否則stoppots將具有相同的散列。

並且您不會將其限制爲前n個字符,因爲否則房屋和房屋將具有相同的散列。

一般hashs取值和一個素數相乘(使得它更容易產生獨特的哈希值),所以,你可以這樣做:

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

我們如何證明或者說這種碰撞非常少? – abhinav 2012-11-17 05:17:56

+0

@jonathanasdf你怎麼能說它總是給你一個唯一的散列鍵。有沒有數學證明?我認爲我們必須對另一個更大的質數進行散列模擬,否則會發生溢出問題。 – devsda 2013-09-04 05:56:43

+12

@devsda他並沒有說獨一無二,他說更有可能是獨一無二的。至於爲什麼,快速搜索谷歌揭示了這篇文章:http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/解釋爲什麼31被用於Java字符串散列。沒有給出數學證明,但它確實解釋了爲什麼素數更好地工作的一般概念。 – Pharap 2013-09-24 00:39:44

14

如果你在Java中這樣做,那麼爲什麼你正在做?只需撥打.hashCode()的字符串

+2

我正在做它作爲類的一部分,並且任務的一部分是寫出幾個不同的哈希函數。教授告訴我們要獲得「更好」的幫助。 – 2010-04-12 18:06:38

+15

如果你需要在JVM版本和實現中保持一致,你不應該依賴'.hashCode()'。相反,使用一些已知的算法。 – 2013-03-26 19:09:18

+4

「String :: hashCode」的算法在JDK中指定,因此它與類java.lang.String的存在一樣可移植。 – yshavit 2015-09-09 12:31:54

5
// djb2 hash function 
unsigned long hash(unsigned char *str) 
{ 
    unsigned long hash = 5381; 
    int c; 

    while (c = *str++) 
     hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ 

    return hash; 
} 

source Logic behind djb2 hash function - SO

+1

我認爲這只是一個首要的數字,所以我們有更少的衝突。 – CornSmith 2013-12-06 03:39:31

29

你或許應該使用String.hashCode()

如果你真的想實現自己的hashCode:

不要試圖排除 對象的顯著部分從 的哈希碼計算,以提高性能 - 約書亞布洛赫有效的Java

只使用前五個字符是一個壞主意。考慮分層名稱,比如URL:它們將具有相同的哈希碼(因爲它們都以「http://」開頭,這意味着它們被存儲在哈希映射中的同一個桶中,表現出糟糕的性能。)

這裏有一個戰爭的故事從「Effective Java」轉述對String的hashCode:

的字符串哈希函數來實現在所有版本 之前1.2檢查 最多十六個字符,均勻 整個串間隔,開始第一個字符爲 。對於分層名稱較大的 集合, 如URLs,這個哈希函數 顯示出可怕的行爲。

+0

如果有人使用雙哈希集合,則可能有必要讓第一個哈希真快又髒。如果一個字符串有一千個長字符串,其中的一半a被一個糟糕的函數映射爲一個特定的值,其中一半映射到不同的值,但單哈希表中的性能會很差,散列表,其中第二個散列檢查整個字符串,可能幾乎是單散列表的兩倍(因爲一半的字符串不必完全散列)。不過,沒有一個標準Java集合可以實現雙重哈希。 – supercat 2014-03-07 23:37:45

+0

有效的Java鏈接已損壞@Frederik – KGs 2017-01-31 22:06:18

109

如果它是一個安全的事情,你可以使用Java加密:

import java.security.MessageDigest; 

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

不錯。我有一個機器學習應用程序,通過大型語料庫進行統計NLP。在對文本中的原始單詞進行形態歸一化之後,我拋棄了字符串值並使用哈希代碼。在整個語料庫中,大約有600,000個獨特的詞,並且使用默認的Java哈希碼功能,我得到了大約3.5%的衝突。但是,如果我SHA-256字符串值,然後從消化的字符串中生成哈希碼,則衝突比率小於0.0001%。謝謝! – benjismith 2010-11-18 18:54:30

+2

感謝您提供有關碰撞和單詞數量的信息。很有幫助。 – philipp 2012-06-12 21:56:07

+8

@benjismith百萬中的一個太大了......是「小於0.0001%」,這是一個說「完全爲0」的傾斜方式?我真的懷疑你看到了SHA-256碰撞,因爲這在任何地方都從未被觀察過;即使對於160位SHA-1也沒有。如果你有兩個產生相同SHA-256的字符串,安全社區會喜歡看到它們;你會以一種非常模糊的方式成爲世界聞名的。請參閱[SHA函數比較](https://en.wikipedia.org/wiki/SHA-2#Comparison_of_SHA_functions) – 2014-02-26 01:03:22

1

如果你想看到的行業標準實現,我想看看java.security.MessageDigest

「消息摘要是安全的單向散列函數,可以獲取任意大小的數據並輸出固定長度的散列值。「

3

FNV-1被傳言是字符串好的哈希函數。

對於長字符串(長於比方說,約200字),你可以得到很好的表現出來的MD4散列函數。作爲密碼函數,它在大約15年前就被破解了,但是對於非加密目的,它仍然非常好,而且速度驚人。在Java的上下文中,你必須將16位的值轉換爲32位的字,例如通過將這些值分組成對,在Java中快速實現MD4可以在sphlib中找到。可能在教室任務的上下文中過度使用,但其他值得一試。

7

Nick提供的這個函數很好,但是如果你使用新的String(byte []字節)進行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()); 
} 

可能這可以幫助別人

+0

您可以將字節數組傳遞給messageDigest.update()。 – szgal 2015-03-05 13:59:34

+0

byteArray2Hex() - 這正是我所期待的!非常感謝:) – Krzysiek 2015-07-29 07:31:31

9

Guava's HashFunctionjsdoc)提供體面的非加密散列強。

+1

它仍然在測試中,因此評論 – ThomasRS 2014-03-13 16:39:17

+1

現在'404''d。 – 2017-05-10 17:45:23

+1

@ShawnS。,我更新了鏈接。 – 2017-05-10 17:49:34

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; 
} 

另一件事,你所能做的就是乘以每個字符INT解析長字(0 * b)+(1 * e)+(2 * a)+(3 * r),這會給你一個int值來玩。上面的第一個哈希函數在「這裏」和「聽到」碰撞,但仍然很棒,給出了一些很好的獨特值。下面的一個不會與「here」和「hear」相撞,因爲我將每個字符與索引相乘,因爲它會增加。

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; 
} 
0

這裏的a link解釋了許多不同的散列函數,現在我希望爲自己的特定問題的ELF散列函數。它需要輸入任意長度的字符串。

2

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; 
} 
-2

下面是我使用的哈希表我建立了一個簡單的哈希函數。它基本上用於獲取文本文件並將每個單詞存儲在表示字母順序的索引中。

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

給予下列文字輸入:阿迪克斯有一天對傑姆說:「我寧願你在後院打錫罐,但我知道你會在鴿子後去 。拍攝所有你想要的藍色鳥,如果你可以擊中它們,但是 記得殺死一隻嘲鳥是一種罪過。「那是我唯一一次聽到阿迪克斯說過做某件事是一種罪過,我問小姐 Maudie關於它。 「你父親是對的,」她說。 「模仿鳥不要 做一件事,除了讓我們享受音樂。他們不吃東西吃人家的花園,不要窩在玉米牀上,他們不會做一件事 但爲我們唱出心聲。這就是爲什麼殺死一隻 模仿鳥是一種罪過。

這將是輸出:

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 don’t don’t don’t do don’t do day 
4 --> eat enjoy. except ever 
5 --> for for father’s 
6 --> gardens go 
7 --> hearts heard hit 
8 --> it’s in it. I it I it’s 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 --> people’s 
16 --> 17 --> right remember rather 
18 --> sin sing said. she something sin say sin Shoot shot said 
19 --> to That’s 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 you’ll you 
25 --> 
26 --> 「Mockingbirds 」 「Your ‘em 「I’d 
+0

一個好的散列函數可以在桶中平均分配值。 – 2017-12-22 21:34:33