文字列を何らかの形式のハッシュに変換する必要があります。これは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/
hash << 5 - hash
は同じですhash * 31 + char
が、LOTはより高速です。とても速いのでいいですし、31は小さな素数です。そこで勝利。
(hash * 31) + char
によって生成される出力は((hash<<5)-hash)+char
、非常に長い文字列(100万を超える文字を含む文字列でテストしました)であっても、シフトベースのコードによって生成される出力と同じであるため、用語で「使用不可能」ではありません精度の。複雑さは、数値ベースバージョンとシフトベースバージョンの両方でO(n)であるため、複雑さの点で「使用不可能」ではありません。
n
最大のものは何n
ですか?
var hashCode = function hashCode (str) {etc...}
?そして、次のように使用しhashCode("mystring")
ますか?
編集
私の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ビットハッシュを使用しても、衝突は遅かれ早かれ発生します。
ハッシュ衝突確率は次のように計算できます。 、としてれます (ここを参照)。これは直感が示唆するよりも高い場合があります
。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
When短い DJ2BとFNV-1aに対して相対的な入力文字列をハッシュ化され、雑音のパフォーマンスが低下し、:http://jsperf.com/32-ビットハッシュ/ 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)
}
注意して使用してください。ただし、あまり期待しないでください。
("0000000" + (hval >>> 0).toString(16)).substr(-8);
ますか?それは同じ(hval >>> 0).toString(16)
ですか?
hval
、(hval >>> 0).toString(16)
ゼロであなたので、パッドには、8文字未満であるかもしれません。(hval >>> 0).toString(16)
常に正確に8文字の文字列が表示されるため、混乱しました。
Math.imul
関数を使用して実装した場合、FNV1aは非常に高速であると言えるでしょう。それだけでもトップベンチマークになり、長期的にはDJB2よりも最終的にはより良い選択になります。
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!'))
str += ""
例外を回避するためにハッシュの前に追加した共有をありがとうstr.split is not a function
hash |= 0
、32ビット整数に変換するために使用します。この実装にはありません。これはバグですか?
答えのほぼ半分はJavaの実装で
String.hashCode
あり、高品質でも超高速でもありません。これは特別なことではなく、文字ごとに31倍されます。1行で簡単かつ効率的に実装でき、次のようにするとはるかに高速になりMath.imul
ます。
hashCode=s=>{for(var i=0,h;i<s.length;i++)h=Math.imul(31,h)+s.charCodeAt(i)|0;return h}
これで問題は解決しました。単純なもので高品質の53ビットハッシュであるcyrb53です。これは非常に高速で、非常に優れたハッシュ分散を提供し、 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ビットハッシュ(相関のない32ビットハッシュが2つ並列)ですが、JavaScriptは53ビット整数に制限されています。必要に応じて、完全な64ビット出力を引き続き使用できます 16進文字列または配列の戻り行を変更するます。
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}
ch
初期化されますか?
'imul'
。
それが誰かを助ける場合、私は上位2つの回答を古いブラウザートレラントバージョンに組み合わせました。これは、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 = "some string to be hashed".hashCode();
String.prototype.hashCode = function(){ var hash = 5381; if (this.length === 0) return hash; for (var i = 0; i < this.length; i++) { var character = this.charCodeAt(i); hash = ((hash<<5)+hash)^character; // Convert to 32bit integer } return hash; }
これは洗練された、より良いパフォーマンスのバリアントです:
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;
}
楽しい!
新しいSubtleCrypto APIについてまだ誰も話していないことに少し驚いています。
文字列からハッシュを取得するには、次のsubtle.digest
メソッドを使用できます。
function getHash(str, algo = "SHA-256") {
let strBuf = new TextEncoder('utf-8').encode(str);
return crypto.subtle.digest(algo, strBuf)
.then(hash => {
window.hash = hash;
// here hash is an arrayBuffer,
// so we'll connvert it to its hex version
let result = '';
const view = new DataView(hash);
for (let i = 0; i < hash.byteLength; i += 4) {
result += ('00000000' + view.getUint32(i).toString(16)).slice(-8);
}
return result;
});
}
getHash('hello world')
.then(hash => {
console.log(hash);
});
var promise = crypto.subtle.digest({name: "SHA-256"}, Uint8Array.from(data)); promise.then(function(result){ console.log(Array.prototype.map.call(new Uint8Array(result), x => x.toString(16).padStart(2, '0')).join('')); });
crypto
正確には機能しません。
mar10の例のおかげで、FNV-1aのC#およびJavascriptで同じ結果を得る方法を見つけました。Unicode文字が存在する場合、パフォーマンスのために上部は破棄されます。現時点ではurlパスのみをハッシュしているため、ハッシュするときにそれらを維持することが役立つ理由がわかりません。
C#バージョン
private static readonly UInt32 FNV_OFFSET_32 = 0x811c9dc5; // 2166136261
private static readonly UInt32 FNV_PRIME_32 = 0x1000193; // 16777619
// Unsigned 32bit integer FNV-1a
public static UInt32 HashFnv32u(this string s)
{
// byte[] arr = Encoding.UTF8.GetBytes(s); // 8 bit expanded unicode array
char[] arr = s.ToCharArray(); // 16 bit unicode is native .net
UInt32 hash = FNV_OFFSET_32;
for (var i = 0; i < s.Length; i++)
{
// Strips unicode bits, only the lower 8 bits of the values are used
hash = hash ^ unchecked((byte)(arr[i] & 0xFF));
hash = hash * FNV_PRIME_32;
}
return hash;
}
// Signed hash for storing in SQL Server
public static Int32 HashFnv32s(this string s)
{
return unchecked((int)s.HashFnv32u());
}
JavaScriptバージョン
var utils = utils || {};
utils.FNV_OFFSET_32 = 0x811c9dc5;
utils.hashFnv32a = function (input) {
var hval = utils.FNV_OFFSET_32;
// Strips unicode bits, only the lower 8 bits of the values are used
for (var i = 0; i < input.length; i++) {
hval = hval ^ (input.charCodeAt(i) & 0xFF);
hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
}
return hval >>> 0;
}
utils.toHex = function (val) {
return ("0000000" + (val >>> 0).toString(16)).substr(-8);
}
ユーザー名と現在の時刻に基づいて一意のIDを生成するには、同様の関数(ただし異なる)が必要でした。そう:
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()
生成:
2DVFXJGEKL
6IZPAKFQFL
ORGOENVMG
... etc
2015年6月の編集:新しいコードには、shortidを使用します:https ://www.npmjs.com/package/shortid
私はサーバーサイド言語を利用していないので、そのようにはできません。
あなたはその方法でそれを行うことができないと確信していますか?
進化を続ける言語であるJavascriptを使用していることを忘れていませんか?
お試しくださいSubtleCrypto
。SHA-1、SHA-128、SHA-256、およびSHA-512ハッシュ関数をサポートしています。
async function hash(message/*: string */) {
const text_encoder = new TextEncoder;
const data = text_encoder.encode(message);
const message_digest = await window.crypto.subtle.digest("SHA-512", data);
return message_digest;
} // -> ArrayBuffer
function in_hex(data/*: ArrayBuffer */) {
const octets = new Uint8Array(data);
const hex = [].map.call(octets, octet => octet.toString(16).padStart(2, "0")).join("");
return hex;
} // -> string
(async function demo() {
console.log(in_hex(await hash("Thanks for the magic.")));
})();
ちょっと遅れましたが、次のモジュールを使用できます:crypto:
const crypto = require('crypto');
const SALT = '$ome$alt';
function generateHash(pass) {
return crypto.createHmac('sha256', SALT)
.update(pass)
.digest('hex');
}
この関数の結果は常に64
文字列です。このようなもの:"aa54e7563b1964037849528e7ba068eb7767b1fab74a8d80fe300828b996714a"
2つのソリューション(ユーザー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();
衝突を回避したい場合は、SHA-256のような安全なハッシュを使用することができます。いくつかのJavaScript SHA-256実装があります。
いくつかのハッシュ実装を比較するためのテストを作成しました。https://github.com/brillout/test-javascript-hash-implementationsを参照してください。
または、http://brillout.github.io/test-javascript-hash-implementations/にアクセスして、テストを実行します。
これは他のいくつかの回答よりも少し安全なハッシュになるはずですが、関数では、プリロードされたソースはありません
私はbasicly縮小さ作成された単純化 SHA1のバージョンを。
文字列のバイトを取り、4〜32ビットの「ワード」でグループ化します。
次に、8ワードごとに40ワードに拡張します(結果に大きな影響を与えるため)。
これは、現在の状態と入力を使用していくつかの計算を行うハッシュ関数(最後の削減)に移動します。私たちはいつも4つの言葉を出します。
これは、ループの代わりにmap、reduce ...を使用したほぼ1つのコマンド/ 1行のバージョンですが、それでもかなり高速です。
String.prototype.hash = function(){
var rot = (word, shift) => word << shift | word >>> (32 - shift);
return unescape(encodeURIComponent(this.valueOf())).split("").map(char =>
char.charCodeAt(0)
).reduce((done, byte, idx, arr) =>
idx % 4 == 0 ? [...done, arr.slice(idx, idx + 4)] : done
, []).reduce((done, group) =>
[...done, group[0] << 24 | group[1] << 16 | group[2] << 8 | group[3]]
, []).reduce((done, word, idx, arr) =>
idx % 8 == 0 ? [...done, arr.slice(idx, idx + 8)] : done
, []).map(group => {
while(group.length < 40)
group.push(rot(group[group.length - 2] ^ group[group.length - 5] ^ group[group.length - 8], 3));
return group;
}).flat().reduce((state, word, idx, arr) => {
var temp = ((state[0] + rot(state[1], 5) + word + idx + state[3]) & 0xffffffff) ^ state[idx % 2 == 0 ? 4 : 5](state[0], state[1], state[2]);
state[0] = rot(state[1] ^ state[2], 11);
state[1] = ~state[2] ^ rot(~state[3], 19);
state[2] = rot(~state[3], 11);
state[3] = temp;
return state;
}, [0xbd173622, 0x96d8975c, 0x3a6d1a23, 0xe5843775,
(w1, w2, w3) => (w1 & rot(w2, 5)) | (~rot(w1, 11) & w3),
(w1, w2, w3) => w1 ^ rot(w2, 5) ^ rot(w3, 11)]
).slice(0, 4).map(p =>
p >>> 0
).map(word =>
("0000000" + word.toString(16)).slice(-8)
).join("");
};
また、出力を16進数に変換して、単語配列ではなく文字列を取得します。
使い方は簡単です。例のため"a string".hash()
に戻ります"88a09e8f9cc6f8c71c4497fbb36f84cd"
16進数の文字列に変換された文字コードを単純に連結した。これは比較的狭い目的に役立ちます。つまり、SHORT文字列(タイトル、タグなど)のハッシュ表現をサーバー側と交換するだけで、関連する理由で、受け入れられたhashCode Javaポートを簡単に実装できません。ここには明らかにセキュリティアプリケーションはありません。
String.prototype.hash = function() {
var self = this, range = Array(this.length);
for(var i = 0; i < this.length; i++) {
range[i] = i;
}
return Array.prototype.map.call(range, function(i) {
return self.charCodeAt(i).toString(16);
}).join('');
}
これは、Underscoreを使用して、より簡潔でブラウザトレラントにすることができます。例:
"Lorem Ipsum".hash()
"4c6f72656d20497073756d"
大きな文字列を同様の方法でハッシュしたい場合は、個々の文字を連結するのではなく、charコードを減らして結果の合計を16進化できると思います。
String.prototype.hashLarge = function() {
var self = this, range = Array(this.length);
for(var i = 0; i < this.length; i++) {
range[i] = i;
}
return Array.prototype.reduce.call(range, function(sum, i) {
return sum + self.charCodeAt(i);
}, 0).toString(16);
}
'One time, I hired a monkey to take notes for me in class. I would just sit back with my mind completely blank while the monkey scribbled on little pieces of paper. At the end of the week, the teacher said, "Class, I want you to write a paper using your notes." So I wrote a paper that said, "Hello! My name is Bingo! I like to climb on things! Can I have a banana? Eek, eek!" I got an F. When I told my mom about it, she said, "I told you, never trust a monkey!"'.hashLarge()
"9ce7"
当然、この方法では衝突のリスクが高くなりますが、reduceで算術をいじることはできますが、ハッシュを多様化して長くしたいと考えました。
まだ誰もしていなかったのでこれを追加しました、そしてこれはハッシュで多く求められ実装されたようですが、それは常に非常に不十分です...
これは、文字列入力と、ハッシュに等しい最大数を受け取り、文字列入力に基づいて一意の数を生成します。
これを使用して、画像の配列に一意のインデックスを作成できます(ランダムに選択されるだけでなく、ユーザーの名前に基づいて選択されるユーザーの特定のアバターを返す場合は、常にその名前を持つユーザーに割り当てられます)。
もちろん、これを使用して、インデックスを色の配列に返すこともできます。たとえば、誰かの名前に基づいて一意のアバター背景色を生成する場合などです。
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);
}
オブジェクトハッシュライブラリなどのすぐに使用できるソリューションの代わりに、この複雑すぎる暗号コードを使用する理由はありません。ベンダーに依存する方が生産性が高く、時間を節約でき、メンテナンスコストを削減できます。
https://github.com/puleos/object-hashを使用するだけです
var hash = require('object-hash');
hash({foo: 'bar'}) // => '67b69634f9880a282c14a0f0cb7ba20cf5d677e9'
hash([1, 2, 2.718, 3.14159]) // => '136b9b88375971dff9f1af09d7356e3e04281951'
var crypto = require('crypto');
。ビルド中に、ベンダーからのこの依存関係コードを縮小バージョンに追加すると思います。