[javascript] Javascript의 문자열에서 해시 생성

문자열을 해시 형태로 변환해야합니다. 이것이 JavaScript에서 가능합니까?

서버 측 언어를 사용하지 않으므로 그렇게 할 수 없습니다.



답변

Object.defineProperty(String.prototype, 'hashCode', {
  value: function() {
    var hash = 0, i, chr;
    for (i = 0; i < this.length; i++) {
      chr   = this.charCodeAt(i);
      hash  = ((hash << 5) - hash) + chr;
      hash |= 0; // Convert to 32bit integer
    }
    return hash;
  }
});

출처 :
http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/


답변

편집하다

내 jsperf 테스트를 기반으로 허용되는 답변이 실제로 더 빠릅니다. http://jsperf.com/hashcodelordvlad

기발한

관심이 있다면 개선 된 (더 빠른) 버전이 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)


답변

참고 : 최상의 32 비트 해시라도 조만간 충돌 발생합니다.

해시 충돌 probablility은 다음과 같이 계산 될 수
1-e ^ (-k (k-1) / 2N로 aproximated,
k ^ 2 / 2N
( 여기 참조 ). 이는 직감이 제안한 것보다 높을 수 있습니다
. 32 비트 해시 및 k = 10,000 항목을 가정하면 확률 1.2 %로 충돌이 발생합니다. 77,163 개의 샘플에서 확률은 50 %가됩니다! ( 계산기 ).
하단에 해결 방법을 제안합니다.

이 질문에 대한 답변으로
고유성과 속도에 가장 적합한 해싱 알고리즘은 무엇입니까? Ian Boyd는 심층 분석 결과를 올렸다 . 한마디로 (내가 해석 한대로), 그는 Murmur가 최고라는 결론에 도달 한 다음 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- 비트 해시 / 3

따라서 일반적으로 murmur3을 권장합니다.
JavaScript 구현에 대해서는 여기를 참조하십시오 :
https://github.com/garycourt/murmurhash-js

입력 문자열이 짧고 분배 품질보다 성능이 더 중요한 경우, esmiralha의 승인 된 답변에서 제안한대로 DJB2를 사용하십시오.

품질과 작은 코드 크기가 속도보다 중요한 경우 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)
}

조심해서 사용하고 너무 많이 기대하지 마십시오.


답변

ES6 에서 허용 된 답변 을 기반으로 합니다. 작고 유지 관리가 가능하며 최신 브라우저에서 작동합니다.

function hashCode(str) {
  return str.split('').reduce((prevHash, currVal) =>
    (((prevHash << 5) - prevHash) + currVal.charCodeAt(0))|0, 0);
}

// Test
console.log("hashCode(\"Hello!\"): ", hashCode('Hello!'));

편집 (2019-11-04) :

한 줄 화살표 기능 버전 :

const hashCode = s => s.split('').reduce((a,b) => (((a << 5) - a) + b.charCodeAt(0))|0, 0)

// test
console.log(hashCode('Hello!'))


답변

답변의 거의 절반은 Java 구현으로 String.hashCode, 고품질도 빠르지도 않습니다. 너무 특별하지는 않습니다. 각 캐릭터마다 31을 곱하면됩니다. 한 줄로 간단하고 효율적으로 구현할 수 있으며 다음과 같이 훨씬 빠릅니다 Math.imul.

hashCode=s=>{for(var i=0,h;i<s.length;i++)h=Math.imul(31,h)+s.charCodeAt(i)|0;return h}

그런데, better- 여기의 뭔가의 밖으로 cyrb53 , 단순하지만 고품질의 53 비트 해시입니다. 매우 빠르며 매우 우수한 해시 분포를 제공하며 모든 32 비트 해시에 비해 충돌 률이 상당히 낮습니다 .

const cyrb53 = function(str, seed = 0) {
    let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
    for (let i = 0, ch; i < str.length; i++) {
        ch = str.charCodeAt(i);
        h1 = Math.imul(h1 ^ ch, 2654435761);
        h2 = Math.imul(h2 ^ ch, 1597334677);
    }
    h1 = Math.imul(h1 ^ h1>>>16, 2246822507) ^ Math.imul(h2 ^ h2>>>13, 3266489909);
    h2 = Math.imul(h2 ^ h2>>>16, 2246822507) ^ Math.imul(h1 ^ h1>>>13, 3266489909);
    return 4294967296 * (2097151 & h2) + (h1>>>0);
};

잘 알려진 MurmurHash / xxHash 알고리즘과 유사 하게 곱셈과 Xorshift 의 조합을 사용 하여 해시를 생성하지만 철저하지는 않습니다. 결과적으로 JavaScript보다 빠르며 구현이 훨씬 간단합니다.

눈사태 (엄격하지 않음)를 달성합니다. 기본적으로 입력의 작은 변화가 출력에 큰 변화가있어 결과 해시가 무작위로 나타납니다.

0xc2ba782c97901 = cyrb53("a")
0xeda5bc254d2bf = cyrb53("b")
0xe64cc3b748385 = cyrb53("revenge")
0xd85148d13f93a = cyrb53("revenue")

동일한 입력의 대체 스트림에 시드를 제공 할 수도 있습니다.

0xee5e6598ccd5c = cyrb53("revenue", 1)
0x72e2831253862 = cyrb53("revenue", 2)
0x0de31708e6ab7 = cyrb53("revenue", 3)

기술적으로 64 비트 해시 (2 개의 상관되지 않은 32 비트 해시 병렬)이지만 JavaScript는 53 비트 정수로 제한됩니다. 필요한 경우 16 진 문자열 또는 배열의 리턴 라인을 변경하여 완전한 64 비트 출력을 계속 사용할 수 있습니다 .

16 진 문자열을 구성하면 성능이 중요한 상황에서 일괄 처리 속도가 크게 느려질 수 있습니다.

return (h2>>>0).toString(16).padStart(8,0)+(h1>>>0).toString(16).padStart(8,0);
// or
return [h2>>>0, h1>>>0];

그리고 재미로, FNV 또는 DJB2보다 높은 품질의 89 개 문자 로 된 최소 32 비트 해시가 있습니다 .

TSH=s=>{for(var i=0,h=9;i<s.length;)h=Math.imul(h^s.charCodeAt(i++),9**9);return h^h>>>9}


답변

그것이 누군가에게 도움이된다면, 상위 2 개의 답변을 구식 브라우저 허용 버전으로 결합 reduce했습니다.

/**
 * @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 = "some string to be hashed".hashCode();


답변

이것은 세련되고 성능이 뛰어난 변형입니다.

String.prototype.hashCode = function() {
    var hash = 0, i = 0, len = this.length;
    while ( i < len ) {
        hash  = ((hash << 5) - hash + this.charCodeAt(i++)) << 0;
    }
    return hash;
};

이것은 Java의 표준 구현과 일치합니다. object.hashCode()

다음은 양의 해시 코드 만 반환하는 것입니다.

String.prototype.hashcode = function() {
    return (this.hashCode() + 2147483647) + 1;
};

그리고 다음은 양의 해시 코드 만 반환하는 Java와 일치하는 것입니다.

public static long hashcode(Object obj) {
    return ((long) obj.hashCode()) + Integer.MAX_VALUE + 1l;
}

즐겨!