簡體   English   中英

在哈希表中創建字符串的哈希值的時間復雜度

[英]Time complexity of creating hash value of a string in hashtable

通常說在哈希表中插入和查找字符串是 O(1)。 但是字符串的哈希鍵是如何制作的呢? 為什么不考慮 O(L),字符串的長度?
我很清楚為什么整數是 O(1),而不是字符串。

我確實理解為什么通常插入哈希表是 O(1),但我對將哈希插入表之前的步驟感到困惑:制作哈希值。

java中如何生成字符串的哈希鍵與C++中的unordered_map之間也有什么區別嗎?
謝謝。

在哈希表中插入等是 O(1),因為它在 table 中的元素數量是恆定

此上下文中的“O(1)”並未說明您計算哈希值的速度有多快。 如果為此付出的努力以某種方式增長,就是這樣。 然而,我發現一個體面的(即“適合這個應用程序”)散列函數的復雜性不太可能比被散列對象的“大小”(即我們的字符串示例中的長度)的線性更糟糕。

通常說在哈希表中插入和查找字符串是 O(1)。 但是字符串的哈希鍵是如何制作的呢? 為什么不是 O(L),字符串的長度? 我很清楚為什么整數是 O(1),而不是字符串。

通常引用的 O(1) 意味着時間不會隨着容器中元素的數量而增長。 正如您所說,從字符串生成哈希值的時間本身可能不是字符串長度的O(1) - 盡管對於某些實現來說是:例如 Microsoft 的 C++ std::hash<std::string>已:

            size_t _Val = 2166136261U;
            size_t _First = 0;
            size_t _Last = _Keyval.size();
            size_t _Stride = 1 + _Last / 10;

            if (_Stride < _Last)
                    _Last -= _Stride;
            for(; _First < _Last; _First += _Stride)
                    _Val = 16777619U * _Val ^ (size_t)_Keyval[_First];
            return (_Val);

_Stride是字符串長度的十分之一,因此固定數量的相距很遠的字符將包含在哈希值中。 這樣的哈希函數在字符串的長度上是 O(1)。

GCC 的 C++ 標准庫采用了不同的方法:至少在 v4.7.2 中,它通過_Hash_impl支持類向下調用static非成員函數_Hash_bytes ,該函數_Hash_bytes合並每個字節的 Murmur 哈希。 因此 GCC 的hash<std::string>在 string 的長度上是 O(N)。

  • GCC 對碰撞最小化的更高優先級在它使用std::unordered_setstd::unordered_map的質數桶中也很明顯,MS 的實現沒有這樣做 - 至少在 VS2013/VC12 之前; 總而言之,MS 的方法對於不易碰撞且負載系數較低的鍵將更輕/更快,但否則會更早更顯着地降級。

並且在java中的hashTable和C++中的unordered_map之間如何生成字符串的哈希鍵有什么區別?

C++ 標准沒有規定如何對字符串進行散列處理——這取決於各個編譯器的實現。 因此,不同的編譯器會做出不同的妥協——甚至是同一編譯器的不同版本。

文檔 David Pérez Cabrera 的回答鏈接到解釋 Java 中的hashCode函數:

返回此字符串的哈希碼。 String 對象的哈希碼計算如下

 s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

使用int算術,其中s[i]是字符串的i字符, n是字符串的長度, ^表示求冪。 (空字符串的哈希值為零。)

這顯然是字符串長度的 O(N)。

快速返回...

通常說在哈希表中插入和查找字符串是 O(1)。

...一個“關鍵”;-P 見解是,在許多問題域中,已知字符串的實際長度變化不大,或者最壞情況長度的散列仍然足夠快。 考慮一個人或公司的名字、街道地址、來自某個源代碼的標識符、編程語言關鍵字、產品/書籍/CD 等名稱:您可以預期 10 億個密鑰需要大約 100 萬倍的內存來存儲第一個千。 使用哈希表,對整個數據集的大多數操作預計會花費一百萬倍的時間。 這將在 100 年后和今天一樣真實。 重要的是,如果某些請求與單個鍵相關,則執行時間不應比使用一千個鍵時​​長得多(假設有足夠的 RAM,並忽略 CPU 緩存效果)——當然,如果它是一個長鍵它可能需要比短密鑰更長的時間,如果您有超低延遲或硬實時要求,您可能會關心。 但是,盡管數據量增加了一百萬倍,但使用隨機密鑰的請求的平均吞吐量將保持不變。

只有當您有一個密鑰大小差異很大的問題域並且密鑰散列時間很重要時,才考慮到您的性能需求,或者您期望平均密鑰大小隨着時間的推移而增加(例如,如果密鑰是視頻流,並且每隔幾個多年來人們一直在提高分辨率和幀速率,從而導致密鑰大小呈指數增長),您是否需要密切關注散列(和密鑰比較)成本。

根據Java的實現,Hashtable使用key(String或Integer)的hashCode方法。 哈希表String.hashCode Integer.hashCode

並且 C++ 根據http://en.cppreference.com/w/cpp/utility/hash使用std::hash<std::string>std::hash<int>並且實現在功能文件(/path /to/c++.../include/c++/4.8/functional)

散列函數的復雜度永遠不會是 O(1)。 如果字符串的長度是 n,那么復雜度肯定是 O(n)。 但是,如果您計算給定數組中的所有散列,則不必進行第二次計算,並且您始終可以通過比較預先計算的散列來在 O(1) 時間內比較兩個字符串。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM