문자열을 해시 형태로 변환해야합니다. 이것이 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은 다음과 같이 계산 될 수
로 aproximated,
( 여기 참조 ). 이는 직감이 제안한 것보다 높을 수 있습니다
. 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;
}
즐겨!