哈希表hash算法的冲突问题
已知字符串的hash算法" title="hash算法">hash算法如下:
function hashCode(str) { let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = hash * 31 + str.charCodeAt(i);
}
return hash;
}
找出2^n个hashCode
方法返回值相同,且长度为2^n的字符串,提示:hashCode('Aa') == hashCode('BB')
。如下图所示:
根据例子可以推论如下:
CC == Bb
DD == Cc
BBBB == BBAa
但是如何找出全部的2^n个字符串还是一点思路都没有
回答:
放在主回答里哈。。。
给个思路哈,我们可以认为最终的hash是31进制数,那么我们可以通过以下步骤来从一个字符串构造另一个hashCode相同的字符串:
1.某个位置上的code-31
2.它的前一个位置上的code+1
为了得到合法的字符串,我们要保证code-31和code+1得到某个字母的编码。例如:'y'的编码值为121,121-31=90刚好为'Z'的编码值。
接下来来构造吧,首先有一个字符串xxxxxxxxyy(这里x代表任意字母)我们把最后一个y-31,得到'Z',倒数第二个y+1,得到'z',所以hashCode("xxxxxxxxyy")==hashCode("xxxxxxxxzZ")。前面n-2个x可以随机有52^(n-2)个。为什么是52,26个大写字母,26个小写字母。
然后最后两个yy改成xx继续上面的过程。
这样的字符串你可以得到非常多个。
================ 以下是原回答 ===================
题目感觉有点矛盾,如果按照hashCode
算法的实现,hashCode('Aa')
肯定不等于hashCode('BB')
。
下面几组也不可能相等。
回答:
这个方法就不该叫hashCode
回答:
大佬的回答然我茅塞顿开,根据您的提示写了下:
function hashCode(str) { let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = hash * 31 + str.charCodeAt(i);
}
return hash;
}
// 寻找hash冲突
const hash1 = hashCode('Aa'); // 65 * 31 + 97
const hash2 = hashCode('BB'); // 66 * 31 + 66
// 某一位上的code-1,后一位的code+31,其他位置不变
// 可以推测出
// CC = Bb
// BBBB = BBAa
console.log(hash1, hash2);
function isAscii(charCode) {
return charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122;
}
function* buildStr(len) {
const list = [];
for (let i = 65; i <= 90; i++) {
list.push(String.fromCharCode(i));
}
for (let i = 97; i <= 122; i++) {
list.push(String.fromCodePoint(i));
}
const chars = new Array(len);
function* _buildStr(chars, pos) {
if (pos >= len) {
return yield chars.join('');
}
for (let i = 0; i < list.length; i++) {
chars[pos] = list[i];
yield* _buildStr(chars, pos + 1);
}
}
return yield* _buildStr(chars, 0);
}
function buildHashCode(n) {
const len = 2 ** n;
let results = [];
const map = {};
let maxHash = 0;
for (const str of buildStr(len)) {
const hash = hashCode(str);
map[hash] = map[hash] || new Set();
const charList = str.split('');
const ret = [];
_buildHashCode(charList, 0, ret);
for (const item of ret) {
map[hash].add(item);
}
if (map[hash].size >= len) {
maxHash = hash;
break;
}
}
// 没有找到指定长度的,返回所有字符串中hash冲突最多的那些字符串
if (maxHash === 0) {
for (const hash of Object.keys(map)) {
maxHash = Math.max(maxHash,map[hash].length);
}
}
return [...map[maxHash]];
function _buildHashCode(charList, pos, ret) {
if (pos + 1 >= charList.length) {
return;
}
let cur = charList[pos];
let next = charList[pos + 1];
let charCodeCur = cur.charCodeAt(0) - 1;
let charCodeNext = next.charCodeAt(0) + 31;
if (isAscii(charCodeCur) && isAscii(charCodeNext)) {
charList[pos] = String.fromCodePoint(charCodeCur);
charList[pos + 1] = String.fromCodePoint(charCodeNext);
ret.push(charList.join(''));
}
_buildHashCode(charList, pos + 1, ret);
charList[pos] = cur;
charList[pos + 1] = next;
}
}
const ret = buildHashCode(2);
for (const str of ret) {
console.log(str, hashCode(str));
}
输出结果:
2112 2112Aaaa 2032736
AabB 2032736
AbBa 2032736
AbCB 2032736
对于种子字符串的生成,我目前采用的是生成字符串的全排列,然后从中选取,不知道你还有别的建议不?
以上是 哈希表hash算法的冲突问题 的全部内容, 来源链接: utcz.com/a/164057.html