如何解决如何使用自定义字母生成GUID,其行为类似于MD5哈希在JavaScript中?
我想知道如何在给定输入字符串的情况下生成GUID,以使相同的输入字符串产生相同的GUID(有点像MD5哈希)。 MD5散列的问题在于它们仅保证低碰撞率,而不是唯一性。相反,我想要这样的东西:
guid('v1.0.0') == 1231231231123123123112312312311231231231
guid('v1.0.1') == 6154716581615471658161547165816154716581
guid('v1.0.2') == 1883939319188393931918839393191883939319
您将如何实现这种事情(最好在JavaScript中)?有可能做吗?我不确定从哪里开始。像uuid module之类的东西不需要种子字符串,并且它们不允许您使用自定义格式/字母。
我不是在寻找canonical UUID format,而是在寻找GUID,理想情况下,它只是由整数组成。。
解决方法
您需要定义文本字符串(例如“ v1.0.0”)到40位长字符串(例如“ 123123 ...”)的一对一映射。这也称为 bijection ,尽管在您的情况下, injection (从输入到输出(不一定是到输入的简单一对一映射))可能就足够了。如您所述,散列函数不一定能确保这种映射,但是还有其他可能性,例如全周期linear congruential generators(如果它们采用种子,您可以一对一映射到输入字符串值)或其他可逆函数。
但是,如果可能的输入字符串的集合大于可能的输出字符串的集合,则由于{,您不能将所有输入字符串与所有输出字符串一一对应(不创建重复项)。 {3}}。
例如,除非您以某种方式限制了120个字符的格式,否则通常无法将所有120个字符的字符串与所有40位数字的字符串一一对应。但是,如果您可以接受将输入字符串限制为不超过10 40 值(大约132位)的方法,或者您可以通过其他方式利用冗余,则可以解决创建40位输出字符串的问题。输入字符串,以确保将它们无损压缩到40个十进制数字(约132位)或更小,这可能是不可能的。另请参见pigeonhole principle。
该算法涉及两个步骤:
- 首先,通过建立字符串的
BigInt
值,类似于另一个答案中给出的charCodeAt()
方法,将字符串转换为stringToInt
。如果任何charCodeAt()
为0x80或更大,或者结果BigInt
等于或大于BigInt(alphabet_length)**BigInt(output_length)
,则会引发错误。 - 然后,通过获取
BigInt
的mod和输出字母的大小并将每个余数替换为输出字母中的相应字符,将整数转换为另一个字符串,直到BigInt
达到0。
一种方法是使用该答案中的方法:
/*
* uuid-timestamp (emitter)
* UUID v4 based on timestamp
*
* Created by tarkh
* tarkh.com (C) 2020
* https://stackoverflow.com/a/63344366/1261825
*/
const uuidEmit = () => {
// Get now time
const n = Date.now();
// Generate random
const r = Math.random(); // <- swap this
// Stringify now time and generate additional random number
const s = String(n) + String(~~(r*9e4)+1e4);
// Form UUID and return it
return `${s.slice(0,8)}-${s.slice(8,12)}-4${s.slice(12,15)}-${[8,9,'a','b'][~~(r*3)]}${s.slice(15,18)}-${s.slice(s.length-12)}`;
};
// Generate 5 UUIDs
console.log(`${uuidEmit()}
${uuidEmit()}
${uuidEmit()}
${uuidEmit()}
${uuidEmit()}`);
只需将Math.random()
调用换成另一个可以获取您的种子值的随机函数。 (目前有很多算法可以创建可播种的随机方法,因此我不会尝试开出一个特定的方法。)
大多数随机种子期望数字,因此您可以通过将字符值相加(将每个字符值乘以10 ^位置,从而始终得到一个唯一的数字)来将种子字符串转换为整数:
const stringToInt = str =>
Array.prototype.slice.call(str).reduce((result,char,index) => result += char.charCodeAt(0) * (10**(str.length - index)),0);
console.log(stringToInt("v1.0.0"));
console.log(stringToInt("v1.0.1"));
console.log(stringToInt("v1.0.2"));
如果您想每次都生成相同的提取字符串,则可以采用与tarkh的uuidEmit()
方法类似的方法,但要摆脱那些变化的位:
const strToInt = str =>
Array.prototype.slice.call(str).reduce((result,0);
const strToId = (str,len = 40) => {
// Generate random
const r = strToInt(str);
// Multiply the number by some things to get it to the right number of digits
const rLen = `${r}`.length; // length of r as a string
// If you want to avoid any chance of collision,you can't provide too long of a string
// If a small chance of collision is okay,you can instead just truncate the string to
// your desired length
if (rLen > len) throw new Error('String too long');
// our string length is n * (r+m) + e = len,so we'll do some math to get n and m
const mMax = 9; // maximum for the exponent,too much longer and it might be represented as an exponent. If you discover "e" showing up in your string,lower this value
let m = Math.floor(Math.min(mMax,len / rLen)); // exponent
let n = Math.floor(len / (m + rLen)); // number of times we repeat r and m
let e = len - (n * (rLen + m)); // extra to pad us to the right length
return (new Array(n)).fill(0).map((_,i) => String(r * (i * 10**m))).join('')
+ String(10**e);
};
console.log(strToId("v1.0.0"));
console.log(strToId("v1.0.1"));
console.log(strToId("v1.0.2"));
console.log(strToId("v1.0.0") === strToId("v1.0.0")); // check they are the same
console.log(strToId("v1.0.0") === strToId("v1.0.1")); // check they are different
请注意,这仅适用于较小的字符串(可能大约10个字符),但是它应该能够避免所有冲突。您可以对其进行调整以处理较大的字符串(从stringToInt
中删除乘法位),但随后可能会发生冲突。
我建议使用MD5 ...
在经典的生日问题之后,在所有事物都相等的情况下,在23个人中,有2个人分享生日的几率是(参见https://en.wikipedia.org/wiki/Birthday_problem)...
为了估算MD5碰撞,我将简化生日问题公式,以帮助预测碰撞的可能性更高。
请注意,尽管在生日问题中,碰撞是肯定的结果,但在MD5问题中,碰撞是否定的结果,因此,提供比预期的更高的碰撞几率可以保守估计MD5碰撞的机会。再加上在某种程度上,这种较高的预测机会可以被认为是MD5输出中任何不均匀分布的忽悠因素,尽管我不相信没有上帝计算机也无法量化这一点...
MD5哈希长度为16个字节,导致范围为256 ^ 16个可能的值。假设MD5算法的结果总体上是一致的,那么假设我们创建一个四千万(即一万亿或10 ^ 15)唯一的字符串来运行哈希算法。然后使用修改后的公式(以简化碰撞计算并添加保守的软糖因子),则碰撞的几率是...
因此,在10 ^ 15或一个四千万个唯一输入字符串之后,哈希冲突的估计赔率与赢得强力球或超级百万大奖的赔率相提并论(每{ {3}})。
还要注意256 ^ 16是340282366920938938463463374607431768211456,它是39位数字,落在40位数字的期望范围内。
因此,建议使用MD5哈希(转换为BigInt),如果确实发生冲突,我将很高兴为您找到一张彩票,只是有机会利用您的运气并拆分收益...
(注意:我使用https://www.engineeringbigdata.com/odds-winning-powerball-grand-prize-r/进行计算。)
,虽然 UUID v4 仅用于生成随机 ID,但 UUID v5 更像是给定输入字符串和命名空间的散列。非常适合您的描述。
正如你已经提到的,你可以使用这个 npm 包:
npm install uuid
而且它很容易使用。
import {v5 as uuidv5} from 'uuid';
// use a UUIDV4 as a unique namespace for your application.
// you can generate one here: https://www.uuidgenerator.net/version4
const UUIDV5_NAMESPACE = '...';
// Finally,provide the input and namespace to get your unique id.
const uniqueId = uuidv5(input,namespace);
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。