我需要将字符串转换为某种形式的哈希。这在JavaScript中是可能的吗?

我没有使用服务器端语言,所以我不能这样做。


当前回答

注意:即使使用最好的32位哈希,冲突也迟早会发生。哈希冲突概率可以计算为,近似为(参见此处)。这可能比直觉所暗示的更高:假设32位哈希和k=10000个项目,则发生冲突的概率为1.2%。77163个样本的概率为50%!(计算器)。我建议在底部使用变通方法。

在回答这个问题时哪种哈希算法最适合唯一性和速度?,伊恩·博伊德发表了一篇很好的深入分析。简而言之(正如我所解释的那样),他得出的结论是MurmurHash是最好的,其次是FNV-1a。esmiralha提出的Java String.hashCode()算法似乎是DJB2的变体。

FNV-1a的分布比DJB2更好,但速度较慢DJB2比FNV-1a更快,但倾向于产生更多的碰撞MurmurHash3比DJB2和FNV-1a更好更快(但优化的实现需要比FNV和DJB2更多的代码行)

这里有一些输入字符串较大的基准测试:http://jsperf.com/32-bit-hash当对短输入字符串进行散列处理时,相对于DJ2B和FNV-1a,杂音的性能会下降:http://jsperf.com/32-bit-hash/3

因此,总的来说,我会推荐杂音3。请参阅此处了解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)
}

小心使用,但不要期望太多。

其他回答

添加这一点是因为还没有人这样做,而这似乎是用哈希来要求和实现的,但它总是做得很糟糕。。。

这需要一个字符串输入和您希望哈希值相等的最大值,并根据字符串输入生成一个唯一的数字。

您可以使用它来生成图像数组中的唯一索引(如果您希望为用户返回一个特定的化身,该化身是随机选择的,但也是基于其名称选择的,因此它将始终分配给具有该名称的人)。

当然,您也可以使用它将索引返回到颜色数组中,例如根据某人的姓名生成独特的化身背景颜色。

function hashInt (str, max = 1000) {
    var hash = 0;
    for (var i = 0; i < str.length; i++) {
      hash = ((hash << 5) - hash) + str.charCodeAt(i);
      hash = hash & hash;
    }
    return Math.round(max * Math.abs(hash) / 2147483648);
}

注意:即使使用最好的32位哈希,冲突也迟早会发生。哈希冲突概率可以计算为,近似为(参见此处)。这可能比直觉所暗示的更高:假设32位哈希和k=10000个项目,则发生冲突的概率为1.2%。77163个样本的概率为50%!(计算器)。我建议在底部使用变通方法。

在回答这个问题时哪种哈希算法最适合唯一性和速度?,伊恩·博伊德发表了一篇很好的深入分析。简而言之(正如我所解释的那样),他得出的结论是MurmurHash是最好的,其次是FNV-1a。esmiralha提出的Java String.hashCode()算法似乎是DJB2的变体。

FNV-1a的分布比DJB2更好,但速度较慢DJB2比FNV-1a更快,但倾向于产生更多的碰撞MurmurHash3比DJB2和FNV-1a更好更快(但优化的实现需要比FNV和DJB2更多的代码行)

这里有一些输入字符串较大的基准测试:http://jsperf.com/32-bit-hash当对短输入字符串进行散列处理时,相对于DJ2B和FNV-1a,杂音的性能会下降:http://jsperf.com/32-bit-hash/3

因此,总的来说,我会推荐杂音3。请参阅此处了解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)
}

小心使用,但不要期望太多。

String.prototype.hashCode=函数(){var散列=0,i、 chr;如果(this.length==0)返回哈希;对于(i=0;i<this.length;i++){chr=this.charCodeAt(i);哈希=((哈希<<5)-哈希)+chr;哈希|=0;//转换为32位整数}返回哈希;}const str='收入'console.log(str,str.hashCode())

来源

如果您想避免冲突,您可能需要使用SHA-256这样的安全散列。有几个JavaScript SHA-256实现。

我编写了测试来比较几个哈希实现,请参见https://github.com/brillout/test-javascript-hash-implementations.

或转到http://brillout.github.io/test-javascript-hash-implementations/,以运行测试。

我看不出有任何理由使用这种过于复杂的加密代码,而不是现成的解决方案,如对象哈希库等。依赖供应商更高效、节省时间并降低维护成本。

只需使用https://github.com/puleos/object-hash

var hash = require('object-hash');

hash({foo: 'bar'}) // => '67b69634f9880a282c14a0f0cb7ba20cf5d677e9'
hash([1, 2, 2.718, 3.14159]) // => '136b9b88375971dff9f1af09d7356e3e04281951'