我須要將字符串轉換爲某種形式的哈希。 這在JavaScript中可行嗎? git
我沒有使用服務器端語言,因此我不能那樣作。 github
編輯 算法
根據個人jsperf測試,可接受的答案實際上更快: http ://jsperf.com/hashcodelordvlad npm
原版的 數組
若是有人感興趣,這是一個改進的(更快的)版本,它將在缺乏reduce
數組功能的舊版瀏覽器上失敗。 瀏覽器
hashCode = function(s){ return s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0); }
單線箭頭功能版本: 服務器
hashCode = s => s.split('').reduce((a,b)=>{a=((a<<5)-a)+b.charCodeAt(0);return a&a},0)
我須要一個相似的函數(但有所不一樣)來根據用戶名和當前時間生成一個惟一的ID。 因此: less
window.newId = -> # create a number based on the username unless window.userNumber? window.userNumber = 0 for c,i in window.MyNamespace.userName char = window.MyNamespace.userName.charCodeAt(i) window.MyNamespace.userNumber+=char ((window.MyNamespace.userNumber + Math.floor(Math.random() * 1e15) + new Date().getMilliseconds()).toString(36)).toUpperCase()
產生: dom
2DVFXJGEKL 6IZPAKFQFL ORGOENVMG ... etc
編輯2015年6月:對於新代碼,我使用shortid: https ://www.npmjs.com/package/shortid jsp
若是對任何人都有用,我會將前兩個答案組合成一個較舊的瀏覽器容忍版本,若是可使用reduce
,則使用快速版本;若是沒有,則使用esmiralha的解決方案。
/** * @see http://stackoverflow.com/q/7616461/940217 * @return {number} */ String.prototype.hashCode = function(){ if (Array.prototype.reduce){ return this.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0); } var hash = 0; if (this.length === 0) return hash; for (var i = 0; i < this.length; i++) { var character = this.charCodeAt(i); hash = ((hash<<5)-hash)+character; hash = hash & hash; // Convert to 32bit integer } return hash; }
用法就像:
var hash = new String("some string to be hashed").hashCode();
注意:即便使用最佳的32位哈希,衝突早晚也會發生。
哈希衝突機率能夠計算爲 ,近似爲 ( 請參見此處 )。 這可能比直覺暗示的要高:
假設一個32位哈希且k = 10,000個項目,則發生碰撞的機率爲1.2%。 對於77,163個樣本,機率變爲50%! ( 計算器 )。
我建議在底部的一種解決方法。
在回答這個問題時, 哪一種哈希算法最適合惟一性和速度? ,伊恩·博伊德(Ian Boyd)進行了深刻的分析 。 簡而言之(據個人解釋),他得出的結論是Murmur最好,其次是FNV-1a。
esmiralha提出的Java String.hashCode()算法彷佛是DJB2的一種變體。
一些帶有較大輸入字符串的基準測試: http : //jsperf.com/32-bit-hash
散列短輸入字符串時,相對於DJ2B和FNV-1a,雜音的性能降低: http ://jsperf.com/32-bit-hash/3
所以,通常而言,我會推薦murmur3。
參見此處以獲取JavaScript實現: https : //github.com/garycourt/murmurhash-js
若是輸入字符串短而且性能比分發質量更重要,請使用DJB2(由esmiralha接受的答案提出)。
若是質量和小的代碼大小比速度更重要,那麼我將使用FNV-1a的此實現(基於此代碼 )。
/** * Calculate a 32 bit FNV-1a hash * Found here: https://gist.github.com/vaiorabbit/5657561 * Ref.: http://isthe.com/chongo/tech/comp/fnv/ * * @param {string} str the input value * @param {boolean} [asString=false] set to true to return the hash value as * 8-digit hex string instead of an integer * @param {integer} [seed] optionally pass the hash of the previous chunk * @returns {integer | string} */ function hashFnv32a(str, asString, seed) { /*jshint bitwise:false */ var i, l, hval = (seed === undefined) ? 0x811c9dc5 : seed; for (i = 0, l = str.length; i < l; i++) { hval ^= str.charCodeAt(i); hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24); } if( asString ){ // Convert to 8 digit hex string return ("0000000" + (hval >>> 0).toString(16)).substr(-8); } return hval >>> 0; }
提升碰撞機率
如此處所述 ,咱們可使用如下技巧擴展哈希位大小:
function hash64(str) { var h1 = hash32(str); // returns 32 bit (as 8 byte hex string) return h1 + hash32(h1 + str); // 64 bit (as 16 byte hex string) }
請謹慎使用,但不要指望太高。
我結合了兩種解決方案(用戶esmiralha和lordvlad)來得到一個函數,該函數對於支持js函數reduce()並仍與舊瀏覽器兼容的瀏覽器應該更快:
String.prototype.hashCode = function() { if (Array.prototype.reduce) { return this.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0); } else { var hash = 0, i, chr, len; if (this.length == 0) return hash; for (i = 0, len = this.length; i < len; i++) { chr = this.charCodeAt(i); hash = ((hash << 5) - hash) + chr; hash |= 0; // Convert to 32bit integer } return hash; } };
例:
my_string = 'xyz'; my_string.hashCode();